• JUC(1)---ReentrantLock和AQS


    Synchronizedjvm内置的锁;而java.util.concurrent包下面的Lock锁是大佬(Doug Lea)用java代码实现的显示锁。

    Juc包下面锁相比jvm内置的锁更加灵活。围绕着AQS(AbstractQueuedSynchronizer)实现了一系列性质的锁,比如共享/独占,公平/非公平,重入,阻塞等待。

    ReentrantLock为例,它有一个字段是sync,是其内部类SyncSync类继承了AbstractQueuedSynchronizer,同时它还有两个子类一个NonfairSync(非公平),一个是FairSync(公平的),显然这两个就是实现分别实现了公平锁和非公平锁。

    再看AbstractQueuedSynchronizer的父类AbstractOwnableSynchronizer中有一个字段是exclusiveOwnerThread,从这个字段注释可以看出来(The current owner of exclusive mode synchronization.)这个是记录独占线程的。

    另外这AbstractQueuedSynchronizer类中还有一个内部类NodeNode中有一系列的标记,还有一个Thread标记当前的线程。借此来实现锁相关队列。比如同步队列借助Nodeprevnext字段来实现的双向链表。条件队列是借助nextWaiter来实现的单向链表。

    同时有一个标记waitStatus来记录当前节点的状态(CANCELLEDSIGNALCONDITIONPROPAGATE)。比方说没抢到锁的就将这个线程新建一个Node扔到队列里面去等着。简单的提了下队列,再说下公平和非公平,公平就是不论怎么样都乖乖的去队列排队等锁;非公平就是上来先去抢锁,抢到就先执行了,没抢到再去队列排队。

     以上这些就是AQS比较重要的点,整个加锁实现的逻辑都是围绕这些东西来玩的。

    上面简单接单介绍AQS中的几个比较关键的点。下面通过代码debug来看下这些东西

    首先我们看下单独运行一个线程的时候 关注ReentrantLock这个类中的sync字段的属性变化。我们会看到一个线程的时候同步器(sync)的head,tail字段(这两个就是上面说到Node类)是null,exclusiveOwnerThread是我们当前的这个这个线程,state字段第一次加锁的时候1,第二次加锁会+1,释放一次锁会-1。当state减到0的时候说明当前线程已经完全释放了锁,以此来实现锁重入的的功能。

    代码片段:

    public class SyncTest {
        
        private Lock lock = new ReentrantLock(true);
    
        public static void main(String[] args) {
            SyncTest syncTest = new SyncTest();
            new Thread(()->syncTest.testLock(), "线程1").start();
        }
    
        public void testLock() {
            lock.lock();
            System.out.println("线程:--->" + Thread.currentThread().getName() + "第一次加锁");
            lock.lock();
            System.out.println("线程:--->" + Thread.currentThread().getName() + "第二次加锁");
            lock.unlock();
            System.out.println("线程:--->" + Thread.currentThread().getName() + "释放第一个锁");
            lock.unlock();
            System.out.println("线程:--->" + Thread.currentThread().getName() + "释放第一个锁");
        }
    
    }

    三个线程执行:

    会看见head 和tail都有值,并且可以看见剩下的两个线程在队列里面,(注意这里head其实只是一个空的头结点,真的头结点是当前执行的线程。这里面head结点主要是为了方便操作声明了)。

    以ReentrantLock中的公平为例:

    1.拿到锁的线程,如果加锁标记state是0,那么就通过CAS操作将state改成1,并且将独占线程设置为当前线程,否则判断当前线程是否是独占线程,如果是则重入

     2.如果都不是 就放入队列中去排队

     

    3.释放锁的时候,当将state修改成0的时候tryRelease方法返回的才是true才会去唤醒后续的结点

     

    juc下面的锁基本就是借助AQS(AbstractQueuedSynchronizer队列同步器的各种实现)和Unsafe这个类调用系统层面的CAS操作保证属性的修改,以及线程阻塞和唤醒操作park和unpark,其次就是很多属性都是使用了volatile来保证一个线程修改其他线程对其状态的更改立马可见。

  • 相关阅读:
    计算机基础
    如何增加一个IT产品的用户黏性
    计算机相关专业一门课程一个案例
    【2012年6月14日】中兴发布Grand(智观)N970
    bmp格式浅析
    3Dbook的使用
    中英文翻译格式
    软件学习书籍推荐
    WEB开发基础
    PHP学习
  • 原文地址:https://www.cnblogs.com/nijunyang/p/12879034.html
Copyright © 2020-2023  润新知