zoukankan      html  css  js  c++  java
  • 有关Java 锁原理

    锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到目标的内容!对Java并发包,锁的实现基本在java.util.concurrent.locks包中,说“基本”是因为,在java.util.concurrent中还有CountDownLatch(可以看成带计数器的锁),CyclicBarrier,Semaphore(类似于信号量,但是也可以看成一个特殊的锁)。这里我们先以java.util.concurrent.locks包为主来讨论锁的问题吧。我们来看看java.util.concurrent.locks包中到底有多少“锁”,主要是这样几个接口及其实现:

    Lock----------

                      |---------ReadLock //读锁

                      |------ReentrantLock  //并发锁

                      |------WriteLock //写锁

    ReadWriteLock----------------

                         |-------------ReentrantReadWriteLock //并发读写锁

    以上:Lock和ReadWriteLock都是接口,是锁行为的契约,其余部分都是实现,而ReadLock和WriteLock是作为ReentrantReadWriteLock的静态内部类存在,不能直接使用,也就是他们是ReentrantReadWriteLock的支持类,不是向外提供给外部程序使用的!

    另外还发现有这样抽象类:

    AbstractOwnableSynchronizer---------

                                                             |----------AbstractQueuedLongSynchronizer

                                                             |-------AbstractQueuedSynchronizer

    这几个都是抽象类,但是没有明显的实现!没有实现?其实是所有的Lock在其实现时使用的基础结构,并且以来它们实现了锁的机制,只要我们先把这几个抽象类的实现逻辑搞清楚,Lock的原理就比较清楚了,理解了原理,使用Lock时就有的放矢了。

    另外包中还有condition接口:

    Condition------------

                                 |--------- ConditionObject

    这个接口和实现是干什么的?是用来实现“竞赛条件”的,提供更加细粒度的线程锁控制,也就是在某个条件下加锁等操作。

    好了,java.util.concurrent.locks 中的基本“布局”和“结构”现在已经比较清楚了,下面我们将从AbstractOwnableSynchronizer抽象类开始讲,这是整个Lock的核心,明白其实现能让你了解如何“准确”的使用锁,而不是仅仅“知道电视机的按钮而不知道里面的原理”,毕竟我们是搞开发的,对原理更应该重视,你说是吧?

    AbstractOwnableSynchronizer

    它依靠于CLH队列来实现锁的机制

         *      +------+  prev +-----+       +-----+

         * head |      | <---- |     | <---- |     |  tail

         *      +------+       +-----+       +-----+

    采用的CHL模型采用下面的算法完成FIFO的入队列和出队列过程,而正是入队和出队的实现“模拟”了“锁”的效用!

    对于入队列(enqueue):采用CAS操作,每次比较尾结点是否一致,然后插入的到尾结点中。

    do {

            pred = tail;

    }while ( !compareAndSet(pred,tail,node) );

    对于出队列(dequeue):由于每一个节点也缓存了一个状态,决定是否出队列,因此当不满足条件时就需要自旋等待,一旦满足条件就将头结点设置为下一个节点。

    while (pred.status != RELEASED) ;

    head  = node;

    有三个核心字段:

    private volatile int state;

    private transient volatile Node head;

    private transient volatile Node tail;

    其中state描述的有多少个线程取得了锁,对于互斥锁来说state<=1。head/tail加上CAS()操作就构成了一个CHL的FIFO队列。下面是Node节点的属性。

    volatile int waitStatus; 节点的等待状态,一个节点可能位于以下几种状态:

    CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。

    SIGNAL = -1: 节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。

    CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。

    0: 正常状态,新生的非CONDITION节点都是此状态。

    非负值标识节点不需要被通知(唤醒)。

    volatile Node prev;此节点的前一个节点。节点的waitStatus依赖于前一个节点的状态。

    volatile Node next;此节点的后一个节点。后一个节点是否被唤醒(uppark())依赖于当前节点是否被释放。

    volatile Thread thread;节点绑定的线程。

    Node nextWaiter;下一个等待条件(Condition)的节点,由于Condition是独占模式,因此这里有一个简单的队列来描述Condition上的线程节点。

    以上只是一个简单的描述吧,只要知道AbstractOwnableSynchronizer的核心是一个CHL队列,而AbstractOwnableSynchronizer是所有Lock的基础,在讲具体Lock的时候,我将“回溯”到AbstractOwnableSynchronizer,凡是具体Lock调用AbstractOwnableSynchronizer的方法的时候,我将具体讲一下其实现,并对应着Lock对应方法的实现,这样就能彻底搞清楚具体Lock的原理了,也就能在使用时游刃有余了。

    对于那些真正需要探知Lock一切底细的家伙而言,我找了个文档,作为附件,是并发作者的论文,不过你需要英文好一些才可以哦

  • 相关阅读:
    shell脚本,文件里面的英文大小写替换方法。
    shell脚本,100以内的质数有哪些?
    shell脚本,当用sed删除某一文件里面的内容时,并追加到同一个文件会出现问题。
    shell脚本,按行读取文件的几种方法。
    shell脚本,锁机制
    shell脚本,通过一个shell程序计算n的阶乘。
    shell脚本,如何写进度条。
    shell脚本,判断给出的字符串是否相等。
    shell脚本,按空格开始60秒的倒计时。
    18:django 日志系统
  • 原文地址:https://www.cnblogs.com/isoftware/p/3794996.html
Copyright © 2011-2022 走看看