ReentrantLock锁相关
1、CAS
多个线程同CAS更新同一个变量,只有一个线程能成功,其他的都失败,但不会挂起,只是通知其他线程再次尝试。
包含三个值:内存值V,进行比较的预期原值A、准备写入的新值B。如果V和A相等,则将V更新为B。
2、AQS(AbstractQueuedSynchronizer)
内部通过Node构成同步队列来完成线程获取锁的排队工作,通过另一个内部类ConditionObject完成等待队列,调用await()后,线程加入等待队列,调用signal()方法后,线程移入同步队列进行锁竞争。
内部通过一个被volatile修饰的变量state来控制同步状态。0-锁未占用 1-锁占用 通过CAS操作来对变量state的改变保证一致性。
通过内部类Node构成FIFO队列来完成线程获取锁的排队工作。
node.prev = pred = pred.prev;
pred.prev:新增的节点之前的节点B的之前的节点A
pred = pred.prev:把A赋值B,原先的pred的pred.prev就变了。
node.prev = pred :让新增的节点中prev的属性指向A
整个表达式相当于node的pred指向都跳过前一个的node。
-------<--------
A | B | NODE
------ | ------ | ------
|prev|<-| |prev| |<--|prev|
|next| |next| |next|
------ ------ ------
park() 阻塞线程 unpark() 唤醒线程
ReentrantLock:(是个可重入锁)
Sync extends AbstractQueuedSynchronizer
非公平锁(默认):NonfairSync endtends Sync 通过一个CAS操作判断是否可以将当前线程设置为拥有锁,不可以则和公平锁一样,排队。
直接cas修改state,如果cas成功,则加锁成功,如果cas没有成功,判断state=0? 如果=0 再判断 队列中是否存在线程Node 如果存在开始入队。。。。
公平锁 : FairSync entends Sync 判断state=0? 如果=0 再判断 队列中是否存在线程Node 如果存在开始入队。。。。
非公平锁和公平锁的区别:由上面的步骤可以看出,非公平锁和公平锁只在刚开始不一样,后续流程是一样的。线程在使用lock()方法加锁时,如果是公平锁,会先检查AQS队列中是否存在线程在排队,如果有线程在排队则当前线程也进行排队,如果是非公平锁,则不会去检查是否有线程在排队,而是直接竞争锁
可以通过重写了AQS的tryAquire和tryRelease方法自定义了获取锁和释放锁的逻辑。
加锁过程:
tf:第一个线程 t2:其他线程
如果是tf,则线程直接持有锁,和AQS队列无关。
如果有其他线程竞争,则会初始化AQS(对内部类Node中的属性初始化:head = node;node.thread = null;node.prev = null;),会在头部虚拟一个Thread=null的Node,t2会自己封装一个Thread!=null的Node,此时队列中除了head以外的Node都在park(阻塞),当tf释放之后unpark(唤醒)某个Node,node被唤醒,如果node是由t2封装的,则首先会把t2的Node设置为head,并通过setThread方法把Thread==null。(t2已经获取到锁了,node就不需要排队了,node中对Thread的引用没有意义了)
ReentrantReadWriteLock :
ReadLock:共享锁模式
WriteLock:独占锁模式
1 public class ReadWriteRockDemo1 { 2 private final static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 3 private final static ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock(); 4 private final static ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock(); 5 private static int count = 1000; 6 private final static ReentrantLock lock =new ReentrantLock(); 7 8 //这样不行,仍然会有负数打印出来 9 public static void main(String[] args) { 10 lock.lock(); 11 while (true) { 12 new Thread(() -> { 13 while (count > 0) { 14 readLock.lock(); 15 System.out.println("####################"); 16 if (count <= 0) { 17 System.out.println("@@@@@@@@@@@@@@@@@@@@" + count); 18 break; 19 } 20 readLock.unlock(); 21 22 writeLock.lock();//此时readLock.lock()会被阻塞,因为writeLock是独占锁,此时可能会有多个readLock被阻塞着 23 //当writeLock锁释放完后,readLock阻塞的多个线程会同时获取锁,同时运行接下来的代码,导致count<0判断失效 24 --count; 25 System.out.println("************" + count); 26 writeLock.unlock(); 27 } 28 }).start(); 29 } 30 31 32 } 33 }
可重入锁:同一个方法中加了多次锁,可以正常执行,不会出现死锁。
A方法锁住了,A方法里面调用了B方法,B方法也锁住了,此时B方法不用等到A方法的锁释放才能调用B方法。
- synchronized
- ReentrantLock
不可重入锁:
Lock lock = new Lock();