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();


posted @ 2021-05-27 11:25  龙之谷2019  阅读(103)  评论(0编辑  收藏  举报