zoukankan      html  css  js  c++  java
  • ReentrantLock

       ReentrantLock : 可以创建  公平 or 非公平锁, 本问主要说非 公平锁

       知,lock, trylock,lockInterruptibly

          这里是架构图,太简单了 不补了

     ReentrantLock 内部主要类

        /**
         * 同步控制的基类.下边有两个子类:非公平和公平
         *.使用了AbstractQueuedSynchronizer类的的一些方法
         */
        static abstract class Sync extends AbstractQueuedSynchronizer
    
        /**
         * 非公平
         */
        final static class NonfairSync extends Sync
    
        /**
         * 公平
         */
        final static class FairSync extends Sync    

       2. 非公平锁的lock 过程

        final void lock() {
            if (compareAndSetState(0, 1)) // 我 插下队, 现在要是没有在用我就直接试一试
                setExclusiveOwnerThread(Thread.currentThread()); // 当前线程OK
            else
                acquire(1); //去得到锁 表示出阻塞了
        }
        public final void acquire(int arg) {
            if (!tryAcquire(arg) && // 尝试的去获取下
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))// 生成Node入队后在轮询等到他可以执行, 如果在这个过程中被中断那么, 当这个线程获得锁的时候出来后会返回true
                selfInterrupt();// 中断了我擦
        }  
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
             final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread(); 
                int c = getState();
                if (c == 0) {// 没人? 我要插队
                    if (compareAndSetState(0, acquires)) {// 插队插队
                        setExclusiveOwnerThread(current);// 插队成功yes
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {// 本来就是锁的拥有者
                    int nextc = c + acquires; // 可重入锁 标记下
                    if (nextc < 0) // overflow 
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
        private Node addWaiter(Node mode) {
            Node node = new Node(Thread.currentThread(), mode);
            // Try the fast path of enq; backup to full enq on failure
            Node pred = tail;
            if (pred != null) {// 存在尾部节点
                node.prev = pred;// 尝试
                if (compareAndSetTail(pred, node)) {// 尝试成为尾部
                    pred.next = node;// OK suc
                    return node;
                }
            }
            enq(node);// 没办法入队 失败了,慢慢来
            return node;
        }
        private Node enq(final Node node) {
            for (;;) { 
    Node t
    = tail; if (t == null) { // 没有元素啊,那我初始化下 if (compareAndSetHead(new Node()))// cas替换下 tail = head; //OK 有头了我 重来下 } else { // 和一开始的入队 逻辑一样的
    node.prev
    = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false;
           // 循环直到 本NODE成为 head成为可执行方式, 没被挂起前是自旋锁
    for (;;) { final Node p = node.predecessor();
    // 前一个节点是 head啊 那么 来吧我们尝试 着成为 拥有者, 因为存在插队情况 所以 会有try的字样。
              // 值得 一提的 是 如果  我被 unpark但是 又被别人抢了, 那我 就 会 自旋,在挂起
    if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && // 捕获失败了是不是要挂起啊
    parkAndCheckInterrupt()) // 挂起 成为重(重量的重)锁, 阻塞等待 unpark。并且检测 是否被中断? interrupted = true;              // 标记 } } finally { if (failed)// 失败 不可能的啊 cancelAcquire(node); } }

    1. CANCELLED,值为1,因为超时或者中断,结点会被设置为取消状态,被取消状态的结点不应该去竞争锁,只能保持取消状态不变,不能转换为其他状态。处于这种状态的结点会被踢出队列,被GC回收;
    2. SIGNAL,值为-1,表示这个结点的继任结点被阻塞了,到时需要通知(unpark)
    3. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
    4. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执,使用在共享模式头结点有可能牌处于这种状态,表示锁的下一次获取可以无条件传播;
    5. 值为0,表示当前节点在sync队列中,等待着获取锁。新结点会处于这种状态。(获取不到缩就会被转换成 SIGNAL)
      
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. 当前的被取消了 */
           // 缩点 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */
    // 从ws -> signal
    compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } private final boolean parkAndCheckInterrupt() { LockSupport.park(this);// 阻塞这个线程 unpark函数可以先于park调用,比如线程A调用unpark函数,给线程B发了一个“许可”,那么当线程B调用park时,它发现已经有“许可”了,那么它会马上再继续运行 而这是 wait notify 所做不到的 return Thread.interrupted(); // 清除中断标记, 并且返回 刚才被中断了么 }

     公平锁和 非公平的差不多, 只不过没有插队的 逻辑, 所以就不说了

     3. unlock过程

        public void unlock() {
            sync.release(1);
        }
        public final boolean release(int arg) {
            if (tryRelease(arg)) { // 尝试 释放
                Node h = head;  // 释放成功, 那么获取头部
                if (h != null && h.waitStatus != 0) // 存在 头节点 且 不是取消的哦状态
                    unparkSuccessor(h); // 对 距离最近的 节点做唤醒操作。
                return true; // suc
            }
            return false; // what?
        }
            protected final boolean tryRelease(int releases) { // 释放
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread()) // 当前县城 没有锁啊 , 没有lock 就 unlock了
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) { // 锁都释放了我去
                    free = true;
                    setExclusiveOwnerThread(null); // 再见 
                }
            // 存在 重入 d setState(c);// 设置状态
    return free; }
       
    1. CANCELLED,值为1,因为超时或者中断,结点会被设置为取消状态,被取消状态的结点不应该去竞争锁,只能保持取消状态不变,不能转换为其他状态。处于这种状态的结点会被踢出队列,被GC回收;
    2. SIGNAL,值为-1,表示这个结点的继任结点被阻塞了,到时需要通知(unpark)
    3. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
    4. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执,使用在共享模式头结点有可能牌处于这种状态,表示锁的下一次获取可以无条件传播;
    5. 值为0,表示当前节点在sync队列中,等待着获取锁。新结点会处于这种状态。(获取不到缩就会被转换成 SIGNAL)
      private void unparkSuccessor(Node node) { // 唤醒 距离 头部 第一个非取消节点。
    /*
             * If status is negative (i.e., possibly needing signal) try
             * to clear in anticipation of signalling.  It is OK if this
             * fails or if status is changed by waiting thread.
             */
            int ws = node.waitStatus;
            if (ws < 0)
                compareAndSetWaitStatus(node, ws, 0);
            /*
             * Thread to unpark is held in successor, which is normally
             * just the next node.  But if cancelled or apparently null,
             * traverse backwards from tail to find the actual
             * non-cancelled successor.
             */
            Node s = node.next; // head的next节点
            if (s == null || s.waitStatus > 0) {// 是空 或者  是取消状态
                s = null;
                for (Node t = tail; t != null && t != node; t = t.prev) // 从后往前 找到那个需要唤醒的 结点。
                    if (t.waitStatus <= 0)
                        s = t;
            }
            if (s != null)
                LockSupport.unpark(s.thread); // 唤醒
        }

        总结  : 我被 unpark但是 又被别人抢了, 那我 就 会 自旋,在挂起 , 所以无论怎么讲这是 多个线程表演的舞台,他们各尽其能去获取, 是通过CAS 控制的 保证的。  有队列,有插队。

    啊四大四大四大

  • 相关阅读:
    Idea中SpringBoot热部署搭建
    Redis 集群搭建
    centos7 vsftp搭建
    Centos 7 安装jdk
    Centos7 安装nginx
    Xshell 连接Linux
    python 的mysql 操作
    NIO/BIO
    java基础-3
    java基础-2
  • 原文地址:https://www.cnblogs.com/shuly/p/7235997.html
Copyright © 2011-2022 走看看