第五章 Java中的锁

5.1   Lock接口

        Lock的意义在于提供了区别于synchronized的另一种具有更多广泛操作的同步方式,它能支持更多灵活的结构,并且可以关联多个Condition对象。

        使用synchronized关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。当然,这种方式简化了同步的管理,可是扩展性没有显示的锁获取和释放来的好。

5.2   AQS

        学习编程两种思路:1,自顶向下:首先从距离用户最近的应用层,逐步往下探索;

                                        2,自下而上:在了解了底层原理之后,再进行应用。

                                学习并发采用自下而上。

                乐观锁的本质即是CAS,操作系统提供了支持CAS修改内存值的原子指令,所以乐观锁得以实现。从开发者的角度去看,虽然底层已经通过CAS实现了乐观锁,Java的底层已经在Unsafe这个类中封装了compareAndSwap方法,支持了对CAS原语的调用,为了使上层更加易用,需要经过进一步的抽象和封装。

                5.2.1 队列同步器的接口与示例

                        AQS的设计是基于模板方法模式的,使用者需要继承同步器并重写指定的方法。

                5.2.2 队列同步器的实现分析
                        1,属性                                                                                                              

                        

                        state用于判断共享资源是否正在被占用的标记位,volatile保证了线程之间的可见性。state的类型是int,为什么不是boolean? 用bolean来表示资源被占用与否语意上不是更明确吗?
                        这里就要谈到线程获取锁的两种模式,独占和共享。当一个线程以独占模式获取锁时,其他任何线程都必须等待,而当一个线程以共享模式获取锁时,其他也想以共享模式获取锁的线程也能够一起访问共享资源,但其他想以独占模式获取锁的线程需要等待。这就说明了,共享模式下,可能有多个线程正在共享资源,所以state需要表示线程占用数量,因此是int值。

                        队列头结点

                        队列尾结点

                        2,内部类       

                        

                        (1)ConditionObject

                        (2)Node:Node中主要存储了线程对象 (thread)、节点在队列里的等待状态 (waitStatus)前后指针(prev、next)等信息。需要重点关注的是waitStatus这个属性,它是一个枚举值(如上图四个不同int值,AOS工作时必然伴随着Node的waitStatus值的变化,如果理解了waitStatus变化的时机,那对理解AOS整个工作原理有很大的帮助。

                        Node两种模式:独占和共享。

                        Node中方法predecessor()方法:

                        

                       返回前置节点,如果为空则抛出空指针异常,前置任务(节点)不能为null时使用。

                        3,方法

                        两种使用场景:
                        1,尝试获取锁,不管有没有获取到,立即返回。
                        2,必须获取锁,如果当前时刻锁被占用,则进行等待。

                        (1)  

                        tryAcquire是一个被 protected 修饰的方法,参数是一个int值,代表对int state的增加操作,返回值是boolean,代表是否成功获得锁。

                        该方法只有一行实现 throw new UnsupportedOperationException(),规定继承类必须override tryAcquire方法,否则就直接抛出UnsupportedOperationException。为什么这里一定需要上层自己实现? 因为尝试获取锁这个操作中可能包含某些业务自定义的逻辑,比如是否“可重入”等。
                        若上层调用tryAcquire返回true,线程获得锁,此时可以对相应的共享资源进行操作,使用完之后再进行释放。如果调用tryAcquire返回false,且上层逻辑上不想等待锁,那么可以自己进行相应的处理;如果上层逻辑选择等待锁,那么可以直接调用acquire方法,acquire方法内部封装了复杂的排队处理逻辑,非常易用。

                        (2)

                        acquire被final修饰,表示不允许子类擅自override,似乎是在表示: 等待并获取锁,我非常可靠,直接用就行,其他您就甭管了。

                        if判断条件包含了两部分:
                                !tryAcquire(arg)
                                acquireQueued(addwaiter(Node.EXCLUSIVE),arg)

                        如果tryAcquire获取锁成功,那么!tryAcquire为false,说明已经获取锁,不用再执行后续判断条件。根据判断条件的短路规则,直接返回。反之执行后续操作。

                        1,addWaiter方法:为当前线程和给定模式创建节点并排入队列。

                        2,acquireQueued方法:以独占模式不间断的模式,为队列中已存在的节点尝试获取锁,由条件等待方法和获取方法使用(?)  

                        

                       

未完....

                        

                        

       

                        

                        

                        

                        

                        

                        

                        
                        

                        

5.3   ReentrantLock

        一句话对ReeentrantLock进行概括: ReentrantLock基于AQS,它实现了公平锁非公平锁,在开发中可以用它对共享资源进行同步。此外,和synchronized一样,ReentrantLock支持可重入,但ReentrantLock在调度上更灵活,支持更多丰富的功能。

                

        1,属性     
        2,内部类
                 
        (1)Sync 
        方法

        1,非final方法

        ,获取锁的操作,为空实现,需要子类根据自己的特征来实现。   

        

        用于反序列化。

        2,final方法(重要)

                (1)

        

        逻辑:1,获取由AQS维护的state值

                   2,如果Sstate为0,则CAS的修改一次state,返回true;

                   3,如果state不为0,说明锁被占用,判断占用线程是否是当前线程(实现可重入性)

                (2)

        

                释放锁(完全或非完全释放)。

        -(2)NonfairSync
        方法

        

        首先尝试CAS的获取锁并修改state,失败则调用AQS的acquire()。

        两个问题:

        1,可重入性

        acquire时会首先调用tryAcquire尝试获得锁,而tryAcquire在NonfairSync中的实现是调用了nonfairTryAcquire(),满足可重入性。2

        

        2,非公平性

        (1)首先CAS的获取锁;

        (2)在nonfairAcquire()中会尝试获得锁。

        (3)FairSync
        方法

        直接调用AQS的acquire()方法

        两个问题:

        1,可重入性

        调用acquire时,会先调用fairSync重写的tryAcquire()方法,其中定义了重入性代码。

        

        2,公平性

        1,在tryAcquire()中,首先判断是否锁是否空闲(c==0)

        2,如果空闲,则调用AQS的判断队列中,除获得锁的节点外是否还有其他节点;

        3,

        3,方法
        4,Java的中断机制

        

        

     

        

        

                        

5.4   LockSupport工具  

5.6   Condition接口

        5.6.1   Condition接口与示例

                Condition定义了等待(await)/通知(signal)两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。      

                                

                                例1                                                                                例2

                Condition中定义的方法如下:

                 

                注意:获取一个Condition必须通过Lock的newCondition()方法。

        5.6.2   Condition的实现与分析

                ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队 列(以下称为等待队列),该队列是Condition对象实现等待/通知功能的关键。       

                        Condition的实现,主要包括:等待队列、等待通知。

                1,等待队列        
                2,等待

                3,通知

                        

                        

                         

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值