java.util.concurrent 学习笔记(1)

1、原子操作Atomic


线程安全的类:当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的。

无状态对象永远是线程安全的。

 

原子操作:多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。

指令重排序:Java语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致

指令重排序的意义:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能。

现代计算机体系和处理器架构都不保证指令出现的顺序执行。



2、volatile语义


(1)Java 存储模型不会对volatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。 

(2)volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程总是可见的,并且不是使用自己线程栈内部的变量。


3、CAS 操作 (Compare and Swap)


独占锁:独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

CAS 操作:CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

ABA问题:一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。


4、锁机制

公平锁和非公平锁:如果获取一个锁是按照请求的顺序得到的,那么就是公平锁,否则就是非公平锁。

Condition接口定义的方法,await*对应于Object.wait,signal对应于Object.notify,signalAll对应于Object.notifyAll。

Condition是与Lock绑定的,所以就有Lock的公平性特性:如果是公平锁为按照FIFO的顺序从Condition.await中释放,如果是非公平锁,那么后续的锁竞争就不保证FIFO顺序了


5、闭锁(Latch)

 

  1. final CountDownLatch startLatch = new CountDownLatch(1);  
  2.         final CountDownLatch overLatch = new CountDownLatch(count);  
  3.         for (int i = 0; i < count; i++) {  
  4.             new Thread(new Runnable() {  
  5.                 public void run() {  
  6.                     try {  
  7.                         startLatch.await();  
  8.                         task.run();  
  9.                     } catch (InterruptedException ex) {  
  10.                         Thread.currentThread().interrupt();  
  11.                     } finally {  
  12.                         overLatch.countDown();  
  13.                     }  
  14.                 }  
  15.             }).start();  
  16.         }  
  17.         long start = System.nanoTime();  
  18.         startLatch.countDown();  
  19.         overLatch.await();  
  20.         System.out.println("END");  

 

 


 

在上面的例子中使用了两个闭锁,第一个闭锁确保在所有线程开始执行任务前,所有准备工作都已经完成,一旦准备工作完成了就调用startLatch.countDown()打开闭锁,所有线程开始执行。第二个闭锁在于确保所有任务执行完成后主线程才能继续进行,这样保证了主线程等待所有任务线程执行完成后才能得到需要的结果。在第二个闭锁当中,初始化了一个N次的计数器,每个任务执行完成后都会将计数器减一,所有任务完成后计数器就变为了0,这样主线程闭锁overLatch拿到此信号后就可以继续往下执行了。


6、CyclicBarrie

CountDownLatch是一次性的,那么CyclicBarrier正好可以循环使用


7、Semaphore

Semaphore是一个计数器,在计数器不为0的时候对线程就放行,一旦达到0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,也就是说Semaphore不是可重入的。每一次请求一个许可都会导致计数器减少1,同样每次释放一个许可都会导致计数器增加1,一旦达到了0,新的许可请求线程将被挂起。


8ReadWriteLock

一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。也就是说读写锁使用的场合是一个共享资源被大量读取操作,而只有少量的写操作(修改数据)


9、内部锁synchronized


10、ConcurrentMap

ConcurrentHashMap:将整个对象列表分为segmentMask+1个片段(Segment)。其中每一个片段是一个类似于HashMap的结构,它有一个HashEntry的数组,数组的每一项又是一个链表,通过HashEntry的next引用串联起来。

ConcurrentSkipListMap:“红黑二叉树”


11、ConcurrentLinkedQueueBlockingQueue BlockingDeque


12、CopyOnWriteArrayList/CopyOnWriteArraySet

思路:一旦对容器有修改,那么就“复制”一份新的集合,在新的集合上修改,然后将新集合复制给旧的引用。当然了这部分少不了要加锁。显然对于CopyOnWriteArrayList/CopyOnWriteArraySet来说最大的好处就是“读”操作不需要锁了。


12、Exchanger

Exchanger提供的是一个交换服务,允许原子性的交换两个(多个)对象,但同时只有一对才会成功。


 

posted @ 2015-09-15 15:39  lihui1625  阅读(115)  评论(0编辑  收藏  举报