1) Java中什么是竞态条件? 举个例子

竞态条件会导致程序在并发情况下出现一些bug。多线程对一些资源的竞争的时候就会产生竞态条件,如果首先要执行的程序竞争失败排到后面执行了, 那么整个程序就会出现一些不确定的bugs。这种bugs很难发现而且会重复出现,因为线程间的随机竞争。一个例子就是无序处理

竞态条件(Race Condition):计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。

最常见的竞态条件为:

一,先检测后执行。执行依赖于检测的结果,而检测结果依赖于多个线程的执行时序,而多个线程的执行时序通常情况下是不固定不可判断的,从而导致执行结果出现各种问题。

压力100问

对于main线程,如果文件a不存在,则创建文件a,但是在判断文件a不存在之后,Task线程创建了文件a,这时候先前的判断结果已经失效,(main线程的执行依赖了一个错误的判断结果)此时文件a已经存在了,但是main线程还是会继续创建文件a,导致Task线程创建的文件a被覆盖、文件中的内容丢失等等问题。

 

多线程环境中对同一个文件的操作要加锁。

和大多数并发错误一样,竞态条件不总是会产生问题,还需要不恰当的执行时序 

 

2)Java中有哪些实现并发编程的方法

1. synchronized关键字

2. 使用继承自Object类的wait、notify、notifyAll方法

3. 使用线程安全的API和集合类:

  • 使用Vector、HashTable等线程安全的集合类
  • 使用Concurrent包中提供的ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue等弱一致性的集合类
  • 在Collections类中有多个静态方法,它们可以获取通过同步方法封装非同步集合而得到的集合,如List list = Collection.synchronizedList(new ArrayList())。

4. 使用原子变量、volatile变量等
5. 使用Concurrent包中提供的信号量Semaphore、闭锁Latch、栅栏Barrier、Callable&Future、阻塞队列BlockingQueue等.
6. 手动使用Lock实现基于锁的并发控制
7. 手动使用Condition或AQS实现基于条件队列的并发控制
8. 使用CAS实现非阻塞的并发控制

  

3)进程间通信的几种方式

1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2. 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

3. 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

4. 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

6. 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

7. 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

 

 

4)ConcurrentHashMap

ConcurrentHashMap是线程安全的HashMap,内部采用分段锁来实现,默认初始容量为16,装载因子为0.75f,分段16,每个段的HashEntry<K,V>[]大小为2。键值都不能为null。每次扩容为原来容量的2倍,ConcurrentHashMap不会对整个容器进行扩容,而只对某个segment进行扩容。在获取size操作的时候,不是直接把所有segment的count相加就可以可到整个ConcurrentHashMap大小,也不是在统计size的时候把所有的segment的put, remove, clean方法全部锁住,这种方法太低效。在累加count操作过程中,之前累加过的count发生变化的几率非常小,所有ConcurrentHashMap的做法是先尝试2(RETRIES_BEFORE_LOCK)次通过不锁住Segment的方式统计各个Segment大小,如果统计的过程中,容器的count发生了变化,再采用加锁的方式来统计所有的Segment的大小。

 

5)ABA问题

ABA问题发生在类似这样的场景:线程1转变使用CAS将变量A的值替换为C,在此时,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍为A,所以CAS成功。但实际上这时的现场已经和最初的不同了。大多数情况下ABA问题不会产生什么影响。如果有特殊情况下由于ABA问题导致,可用采用AtomicStampedReference来解决,原理:乐观锁+version。可以参考下面的案例来了解其中的不同

压力100问
 1 public class ABAQuestion
 2 {
 3     private static AtomicInteger atomicInt = new AtomicInteger(100);
 4     private static AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<Integer>(100,0);
 5  
 6     public static void main(String[] args) throws InterruptedException
 7     {
 8         Thread thread1 = new Thread(new Runnable(){
 9             @Override
10             public void run()
11             {
12                 atomicInt.compareAndSet(100, 101);
13                 atomicInt.compareAndSet(101, 100);
14             }
15         });
16  
17         Thread thread2 = new Thread(new Runnable(){
18             @Override
19             public void run()
20             {
21                 try
22                 {
23                     TimeUnit.SECONDS.sleep(1);
24                 }
25                 catch (InterruptedException e)
26                 {
27                     e.printStackTrace();
28                 }
29                 boolean c3 = atomicInt.compareAndSet(100, 101);
30                 System.out.println(c3);
31             }
32         });
33  
34         thread1.start();
35         thread2.start();
36         thread1.join();
37         thread2.join();
38  
39         Thread thread3 = new Thread(new Runnable(){
40             @Override
41             public void run()
42             {
43                 try
44                 {
45                     TimeUnit.SECONDS.sleep(1);
46                 }
47                 catch (InterruptedException e)
48                 {
49                     e.printStackTrace();
50                 }
51                 atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp()+1);
52                 atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp()+1);
53             }
54         });
55  
56         Thread thread4 = new Thread(new Runnable(){
57             @Override
58             public void run()
59             {
60                 int stamp = atomicStampedRef.getStamp();
61                 try
62                 {
63                     TimeUnit.SECONDS.sleep(2);
64                 }
65                 catch (InterruptedException e)
66                 {
67                     e.printStackTrace();
68                 }
69                 boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp+1);
70                 System.out.println(c3);
71             }
72         });
73         thread3.start();
74         thread4.start();
75     }
76 }
AtomicStampedRefrence

相关文章:

  • 2021-09-26
  • 2021-05-21
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-01-17
  • 2021-05-17
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-23
  • 2021-08-15
相关资源
相似解决方案