JUC--010--locks6

前面已经说过了 ReentrantLock 的四种获取锁的方式,也说过了公平和非公平的区别。
现在来看一下条件 Condition。 Condition 让锁的使用更加的灵活。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Condition 是一个接口, 有两个已知实现类:
AbstractQueuedLongSynchronizer::ConditionObject
AbstractQueuedSynchronizer::ConditionObject

Condition 全部的代码如下:
public interface Condition 
{
    void await() throws InterruptedException;
    void awaitUninterruptibly();
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    boolean awaitUntil(Date deadline) throws InterruptedException;
    void signal();
    void signalAll();
}

从方法上看,Condition 的功能有2个, await 和 signal
而且这个两个方法的调用都是需要在有锁的环境下进行的。也就是说,如果一个线程
在没有获取锁的情况下就使用这两个方法会抛出异常。具体在哪里抛出异常,需要看源码。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
按照 Condition java doc 写出了简化版的生产者消费者,这个作为源码分析的用户代码
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
package cn.rulebreaker.juc.mylock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Lock5App
{
    private ReentrantLock lock = new ReentrantLock();
    private Condition cdProduct = lock.newCondition();
    private Condition cdConsume = lock.newCondition();
    private int count;

    private void t1()
    {
        Runnable tProduct = () ->
        {
            for(int i=0; i<100; ++i) {
                lock.lock();
                try
                {
                    if(count > 9) {
                        cdProduct.await();
                    }
                    count++;
                    System.out.println("生产者生产:" + count);
                    cdConsume.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        Runnable tConsume = () -> {
            for (int i=0; i<100; ++i) {
                lock.lock();
                try {
                    if(count < 1) {
                        cdConsume.await();
                    }
                    System.out.println("消费产品:" + count);
                    count--;
                    cdProduct.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        Thread t1 = new Thread(tProduct, "生产线程");
        Thread t2 = new Thread(tConsume, "消费线程");

        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        Lock5App lock5App = new Lock5App();
        lock5App.t1();
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Condition 的其中一个实现了:AbstractQueuedSynchronizer::ConditionObject
只有一个无参构造方法。而且这个实现类继承父类的方式是公开的,其他除了构造方法外
全部是私有的。
里面有两个属性:
private transient Node firstWaiter;
private transient Node lastWaiter;
像不像 head 和 tail
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//创建一个 条件, 直接会调用到 Condition 的构造方法,构造方法中没有代码。
Condition cdProduct = lock.newCondition();

cdProduct.await();

public final void await() throws InterruptedException {
    //如果线程是中断状态,直接抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //创建一个node, 并放到这个条件中的一个单项链表中
    Node node = addConditionWaiter();         //-----------1
    
    //彻底释放锁,不管重入了多少次,一次性全部释放
    //而且唤醒 head 后面的那个线程起来抢锁了
    int savedState = fullyRelease(node);      //-----------2
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {            //-----------3
        //这个节点不再一个链表中 ???
        //无限阻塞当前线程。下面的代码就先不看了,等解除阻塞以后再来看
        LockSupport.park(this);               //-----------4
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------1:
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        
        // If lastWaiter is cancelled, clean out.
        //如果最后一个节点不为空,但是状态不对, 那就检测一下整个列表,
        //把状态不对的全部清除
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters();   //-----------1.1
            
            //unlinkCancelledWaiters 内部可能会更新 lastWaiter 的值
            t = lastWaiter;
        }
        
        //创建一个状态时 CONDITION 的 Node
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        
        //把新创建的 Node 加到这个条件的单向列表中。 并返回这个 Node
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }
    
    //-----------1.1
    /* 
     * 把链表中(他只是使用 Node.nextWaiter, 并没有 Node.prevWaiter, 所以 
     * Node 关于 Condition 的实现是单项链表)状态不是 CONDITION 的给清除掉
     */
    private void unlinkCancelledWaiters() {
        Node t = firstWaiter;
        Node trail = null;
        while (t != null) {
            //获取当前节点的下一个节点
            Node next = t.nextWaiter;
            if (t.waitStatus != Node.CONDITION) {
                //当前节点状态不对, 那么当前节点是需要删除的节点
                //先把他的后一个节点指向清空。 后一个节点现在就是 next
                t.nextWaiter = null;
                
                //如果上一个状态是 CONDITION 节点也是 null。
                //说明当前 从头开始找,还没有找到一个状态符合的节点
                //所以现在的 firstWaiter 可以直接是 下一个节点了
                if (trail == null)
                    firstWaiter = next;
                
                //存在状态符合的节点,那么把 当前节点的下一个节点
                //直接放到符合的那个节点的下一个节点,因为本节点要删除了
                else
                    trail.nextWaiter = next;
                //当前节点的下一个节点为空,说明节点没有了。那么最后一个节点就是 trail
                if (next == null)
                    lastWaiter = trail;
            }
            else
                //状态是 CONDITION, trail 持有当前的 节点
                //所以在循环中,trail 上次循环状态满足的节点
                trail = t;
                
            //当前节点变成直接的下一个节点, 接着继续循环了
            t = next;
        }
    }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------2
    //AQS::fullyRelease  外部类 AQS 的方法
    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            //因为已经加锁了,所以 savedState 这个值是大于0的
            int savedState = getState();
            if (release(savedState)) {  //-----------2.1
                failed = false;
                
                //释放锁成功, 返回 savedState 。
                //因为锁可能被重入,所以这个值 要保管好。因为用户重入几次锁,
                //就会 unlock 几次锁。这里如果不记录重入几次, 那么用户调用
                //unlock 次数不匹配就异常或者无法彻底释放锁。
                return savedState;
            } else {
                //没有成功释放锁,不正常啊,抛出异常
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                //如果出异常,设置状态为 CANCELLED
                node.waitStatus = Node.CANCELLED;
        }
    }
    
    //-----------2.1
    public final boolean release(int arg) {
        //尝试释放锁,而且本线程可能重入多次, arg 可能会大于1
        //但是经过这个方法,正常应该会把 state 状态清0 ,即释放锁。
        if (tryRelease(arg)) {    //-----------2.1.1
            Node h = head;
            
            //await 释放锁以后, 那些阻塞在获取锁的线程(那个双向链表)
            //找到 head 后面的那一个,唤醒他
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
    //-----------2.1.1  这个方法 ReentrantLock::Sync 复写
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            /*
             * 这里有一个异常,如果调用 await 的线程并没有获取锁就会在这抛异常
             * 所以,await 方法必须在 有锁的环境下才能调用。
             */
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------3
    //AQS
    final boolean isOnSyncQueue(Node node) {
        //参数 node 就是刚才创建的 CONDITION Node, 
        //如果锁 这里状态不是 CONDITION 了,难道被取消了
        //而且 prev 和 next 这两个数据我们都没使用它啊,
        //包括下面的方法 findNodeFromTail 怎么能从 那个线程阻塞列表中找条件节点呢?
        //可能这个方法不是为这里使用的。或许不是这个时间点使用的,继续吧
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;

        return findNodeFromTail(node);  //-----------3.1
    }
    
    //-----------3.1
    //从尾部开始找 参数 node 是否在双向链表中。
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------4:
LockSupport.park(this);
当前线程调用了 condition.await() 方法, 执行到这里的时候就阻塞在这。
此时需要查看代码如何从这里解除阻塞的。 从上面的代码可以看出,调用 await 的时候
释放了当前线程占有的锁(可能被重入多次),然后就阻塞了,当前线程只是在 条件的单向
列表中添加了节点,但是并没有在阻塞的双向链表添加节点。所以其他线程获取锁,并释放
锁,本线程也是没有可能重新获取锁的(不在阻塞队列)。所以当前线程解除阻塞不是其他
线程释放锁。而应该在条件的signal()。【后续的剧情会再次反转】
================================================================================


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值