1、CAS是乐观锁,在concurrentHashmap中的putval方法中会用到CAS
非阻塞的实现CAS:CAS指令需要3个操作数,分别是内存地址(在JAVA中理解为变量的内存地址,用V表示),旧的预期值(用A表示),新值(B)。CAS指令执行时,当且仅当V处的值符合旧预期值A时,处理器用B更新A值,否则它就不执行更新,但是无论是否更新了V处的值,都会返回V的旧值,上述的处理过程是一个原子操作。
CAS缺点 :ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是一个值原来是A,变成B,又变成A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加版本号,每次变量更新的时候把版本号加一,那么A-B-A就变成了1A-2B-3C。
**2、CAS还有可能引发的问题**: 1.ABA问题; 2.不停的自旋等待,造成处理器资源浪费; (可以通过加sleep+自旋,这样可以让线程进入睡眠) 3.CAS只能保证对一个变量操作的原子性,而不能保证对代码块操作的原子性
当许多线程都可以进入一个方法内对变量进行修改的时候,由于线程数量过于密集,导致大部分进程都无法成功修改变量的值,白白在那里循环消耗着资源。所以java对它进行了优化引入了一个cell[]数组,比如有5个线程的话,就会让他进行CAS自增。如果是100个线程的话,就会分配到不同的cell数组当中,每一组cell对其中的一个元素进行自增操作,这样每组10个元素的值都是10,系统再把这10个数组的值进行汇总得到100,就相当于100个线程对I进行了100次自增。
AQS原理
AQS的核心原理是:当一个共享资源空闲的时候,就将当前的请求线程设置为工作线程,并把共享资源加锁,还有请求该同步资源的线程就会进入阻塞状态。这就需要维护一个可以维护等待状态和唤醒获取锁的机制。可以通过双向队列CLH和CAS来实现,把没有获取锁的线程放到队列的末尾,然后自旋等待获取锁,如果是头结点后面的节点,就会不断自旋并获取前面节点的同步状态,如果前面的节点释放锁了,它就会被唤醒并获得锁,并走出队列,当它的线程逻辑实现完了就会把锁交给它后面的线程。
private volatile int state;//共享变量,使用volatile修饰保证线程可见性
AQS属性(Node head Node tail, int state)等待的线程被封装为node实例
非公平锁的模式下的线程的上下文切换少了,因此性能开销小了些,公平锁保证了FIFO原则,代价是要大量的线程切换,非公平锁虽然可能造成线程饥饿,但是减少了线程切换性能更高,保证了吞吐量。
讲一下AQS是如何阻塞线程的
当线程获取同步资源失败,进入CLH队列的时候,它通过自旋的方式不断的获取同步状态。它首先是在判断当前线程的前驱节点的状态是不是singal,如果是的话,那么当前线程就会被阻塞,就会调用LockSupport的park()方法阻塞该线程。
当线程释放同步状态之后,就需要唤醒该线程的后继节点,调用unparkSuccessor或者lockSupport.unpark()方法唤醒线程。
AQS的应用:reentrantlock,semmaphore
semmaphore信号量用它来表示剩余的许可证的数量,reentrantlock用它来表示线程已经请求了多少次锁。
CountDownLatch会维护一个count,通过CAS操作来减少count,当count为0的时候,可以执行await()方法后的线程。所以CountDownLatch可以实现让一个线程等待一组线程完成之后再执行。
CountDowmLacth有两种用法
(1)某一个线程等待N个线程执行完毕,先将CountDowmLatch的计数器设置为N,每执行完一个线程,count-1,当count为0的时候,可以执行await()方法的线程。
(2)实现多个线程并发执行任务。可以将CountDownLatch的计数器初始化为1,然后多个线程开始执行任务之前首先调用await()方法,当主线程调用countdown()方法的时候,计数器变为0,多个线程就同时开始执行。
Semaphore信号量
可以指定多个线程同时访问某个资源。
可以设置N个信号量,执行**semaphore.acquire(count)**方法,就会尝试获取count个许可证,如果不够的话,多余的线程就会进入阻塞状态,执行semapphore.relase(count)就会释放count个许可证。
CyclicBarrier的用法
循环屏障,它会使多个线程到达屏障时被阻塞,直到最后一个线程到达屏障的时候,所有被屏障拦截的线程就会继续开始工作。每个线程调用await()方法来表示线程到达屏障,然后被阻塞。
CountDownLatch和CyclicBarrier的区别
CountDownLatch是基于AQS实现的,CyclicBarrier是基于reetrantlock实现的。
CountDownLatch是类似于计数器,只能使用一次。CyclicBarrier计数器具有Reset功能,可以多次使用。对于CountDownLatch,是一个线程(或者多个线程)在等待,等到其他N个线程执行结束之后,它就开始执行。而CyclicBarrier是多个线程互相等待,直到最后一个线程到达同一个同步点,然后继续一起执行。



