JUC-Lock

简介: JUC-Lock

JUC-Lock

  • Lock接口:
  • Lock接口定义了比同步原语更灵活、更轻巧锁机制;同时,支持多种条件关联锁
  • JUC的Lock接口的基本实现为ReentrantLock
  • ReentrantLock支持公平、非公平、可重入互斥同步锁

ReentrantLock

1.ReentrantLock分为两种实现,非公平以及公平模式;

2.公平模式基于CAS+队列实现,而非公平模式则是仅基于CAS实现

3.ReentrantLock中的队列,本质上是基于AQS实现的;而AQS本质上只是一个维持了内存可见的state的双向队列

  • ReentrantLock中Node的状态字

//代表当前线程取消竞争锁    
        static final int CANCELLED =  1;
        //代表当前节点正常
        static final int SIGNAL    = -1;
        //代表当前线程在等待条件
        static final int CONDITION = -2;
        //无法理解.......
        static final int PROPAGATE = -3;
        //表示状态字;只能用于以上四个状态字以及0
        volatile int waitStatus;
  • ReentrantLock构造方法
//默认为非公平实现
public ReentrantLock() {
        sync = new NonfairSync();
    }
//通过传入fair变量控制;
//fair=true,公平锁实现;否则非公平实现
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  • sync是什么?
  • Sync是基于AQS实现的ReentrantLock锁同步控制基础
  • 如下:
  • ReentrantLock#lock()方法

 

final void lock() {
            acquire(1);
        }
  • ReentrantLock中acquire方法基于AQS
public final void acquire(int arg) {
        //至少尝试获取一次锁;
        //如果锁没有获取成功,则将当前线程加入等待队列;
        //并且中断当前线程
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
• ReentrantLock#tryAcquire(int args)
  • 尝试获取锁的公平版本
  • 非公平版本其实就是略去了公平版本的队列判断:hasQueuedPredecessors()
protected final boolean tryAcquire(int acquires) {
            //当前线程ID
            final Thread current = Thread.currentThread();
            //当前锁状态
            int c = getState();
            //如果当前锁无人占有
            if (c == 0) {
                //公平锁讲究先来后到
                //如果当前等待队列没有等待线程,
                //且通过CAS成功改变锁的状态
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    //设置当前锁持有的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如当前事务请求线程与当前事务执行线程是一样的;可重入锁特性
            //这里就是可重入锁特性:同一线程可以重复获取同一把锁
            else if (current == getExclusiveOwnerThread()) {
                //那么将当前锁持有状态+1
                //通过设置acquires=1来完成可重入锁特性
                int nextc = c + acquires;
                //这里对可重入锁的限制,当同一线程持有锁达到int的上限时;
                //会抛出异常
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                 //并且重新改变state   
                setState(nextc);
                return true;
            }
            return false;
        }
• ReentrantLock#addWaiter()
• 将当前等待线程以排它模式入队
    private Node addWaiter(Node mode) {
        //以当前线程建立Node,并且设置Node的模式为排它
        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;
            //通过CAS修改队尾指针,新增等待线程
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //通过自旋追加节点
        enq(node);
        return node;
    }
  • ReentrantLock#acquireQueued()
  • 通过自旋不停的获取锁

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //获取p的前置节点
                final Node p = node.predecessor();
                //如果p的前置节点为head哑节点
                //则说明p节点为阻塞队列的对头元素
                //在AQS的CLH队列中,只有队头元素才能抢占锁
                //且尝试获取锁成功,则返回false
                //当返回false时,则说明当前线程不需要挂起
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //如果事情发展到这一步;
                //则说明前一步骤中,当前线程不在队列的头部
                //或者没有抢占锁成功,则需要将当前线程挂起
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            //当自身获取锁出现异常时
            if (failed)
                cancelAcquire(node);
        }
    }
• ReentrantLock#shouldParkAfterFailedAcquire()
• 检验当前线程是否需要挂起
• 每个线程第一次进入该方法时,都为false;当第二次进入时才为true

 

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //前驱节点状态字
        int ws = pred.waitStatus;
        //前驱节点正常;我需要挂起
        if (ws == Node.SIGNAL)//-1
            return true;
        //当状态字大于0,说明前驱节点取消竞争锁;
        //此时不停的寻找状态字小于0的节点;
        //因为前驱节点的状态字影响了自身的行为;
        //自身线程的行为依赖于前驱节点的状态字 
        if (ws > 0) {//1
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;    
        } else {
            //当一个新的节点进入队列时,默认status为0;
            //因此需要将前驱节点的status设置为-1;
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
  • ReentrantLock#parkAndCheckInterrupt()
  • 挂起当前线程

   

private final boolean parkAndCheckInterrupt() {
        //挂起当前线程
        LockSupport.park(this);
        return Thread.interrupted();
    }
  • ReentrantLock#release()
  • 释放当前线程,唤醒等待线程
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                //唤醒后继节点线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  • ReentrantLock#tryRelease()
  • 尝试释放当前线程
protected final boolean tryRelease(int releases) {
            //state表示锁状态;亦是当前线程持有锁的数目
            //可重入锁的释放    
            int c = getState() - releases;
            //如果当前线程不是锁的持有者
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果说c==0;则说明当前线程持有的锁已经完全释放
            //可重入锁,一个线程可以多次请求同一把锁
            if (c == 0) {
                free = true;
                //设置当前锁的持有线程为null
                setExclusiveOwnerThread(null);
            }
            //修改状态位
            setState(c);
            return free;
        }
  • ReentrantLock#unparkSuccessor()
  • 唤醒后继线程

 

private void unparkSuccessor(Node node) {
        //head节点的状态
        int ws = node.waitStatus;
        if (ws < 0)
            //修改head节点的状态,以影响后继节点
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            //从后面往前面找,找最接近头节点的小于0的线程
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //如果存在状态位小于0的线程
        if (s != null)
            //唤醒线程
            LockSupport.unpark(s.thread);
    }
目录
相关文章
|
3月前
|
Java
Java中ReentrantLock中 lock.lock(),加锁源码分析
Java中ReentrantLock中 lock.lock(),加锁源码分析
27 0
|
2月前
|
监控 安全 Java
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
14 0
|
3月前
|
存储 安全 算法
掌握Java并发编程:Lock、Condition与并发集合
掌握Java并发编程:Lock、Condition与并发集合
31 0
|
12月前
|
算法 Java
JUC--锁
简单介绍锁
|
Java
JUC基础(三)—— Lock锁 及 AQS(2)
JUC基础(三)—— Lock锁 及 AQS
81 0
|
算法 调度
JUC基础(三)—— Lock锁 及 AQS(1)
JUC基础(三)—— Lock锁 及 AQS
106 0
【JUC基础】04. Lock锁
java.util.concurrent.locks为锁定和等待条件提供一个框架的接口和类,说白了就是锁所在的包。
5504 0
线程同步的方法:Synchronized、Lock、ReentrantLock分析
线程同步的方法:Synchronized、Lock、ReentrantLock分析
|
安全 Java 调度
java常见锁Reentrantlock,synchronized,SpinLock,ReadWriteLock
公平锁―是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后中请的线程比先中请的线程优先获取锁。在高并发的情况下,有可能会造成优先级反转或者饥饿现象
138 0