并发(四):ReentrantLock之条件等待队列

268 阅读4分钟

大纲内容****

  • 条件等待队列
  • 生产者-消费者Demo

\

image.png

详细内容可以看下链接, 关注一波, 后期分享经典面试题

并发(四):ReentrantLock之条件等待队列

并发(四):ReentrantLock之条件等待队列

并发(四):ReentrantLock之条件等待队列

并发(四):ReentrantLock之条件等待队列

并发(四):ReentrantLock之条件等待队列


条件等待队列并发(三):ReentrantLock类中,谈到了锁,每个ReentrantLock锁对应很多个条件等待队列,而synchronized锁只存在一个条件等待队列。\

//图中的ReentrantLock定义了两个条件等待队列。public class Bamboo {    private int bambooCount = 0;    private boolean flag = false;
    Lock lock = new ReentrantLock();    Condition producerCondition = lock.newCondition();    Condition consumerCondition = lock.newCondition();

    public void producerBamboo() {        lock.lock(); // 获取锁资源        try {            while (flag) { // 如果有竹子                try {                    producerCondition.await(); // 挂起生产竹子的线程                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            bambooCount++; // 竹子数量+1            System.out.println(Thread.currentThread().getName() + "....生产竹子,目前竹子数量:" + bambooCount);            flag = true; // 竹子余量状态改为true            consumerCondition.signal(); // 生产好竹子之后,唤醒消费竹子的线程        } finally {            lock.unlock(); // 释放锁资源        }    }
    public void consumerBamboo() {        lock.lock(); // 获取锁资源        try {            while (!flag) { // 如果没有竹子                try {                    consumerCondition.await(); // 挂起消费竹子的线程                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            bambooCount--; // 竹子数量-1            System.out.println(Thread.currentThread().getName() + "....消费竹子,目前竹子数量:" + bambooCount);            flag = false; // 竹子余量状态改为false            producerCondition.signal(); // 消费完成竹子之后,唤醒生产竹子的线程        } finally {            lock.unlock(); // 释放锁资源        }    }}
/** * ------------------分割线-------------------- **/// 测试类class ConditionDemo {    public static void main(String[] args) {        Bamboo bamboo=new Bamboo();        // 生产者线程组        Thread t1 = new Thread(()->{            bamboo.producerBamboo();        }, "生产者-A");        Thread t2 = new Thread(()->{            bamboo.producerBamboo();        }, "生产者-B");        Thread t3 = new Thread(()->{            bamboo.producerBamboo();        }, "生产者-C");
        // 消费者线程组        Thread t4 = new Thread(()->{            bamboo.consumerBamboo();        }, "消费者-D");        Thread t5 = new Thread(()->{            bamboo.consumerBamboo();        }, "消费者-E");        Thread t6 = new Thread(()->{            bamboo.consumerBamboo();        }, "消费者-F");
        t1.start();        t2.start();        t3.start();        t4.start();        t5.start();        t6.start();    }}输出:生产者-B....生产竹子,目前竹子数量:1消费者-D....消费竹子,目前竹子数量:0生产者-A....生产竹子,目前竹子数量:1消费者-E....消费竹子,目前竹子数量:0生产者-C....生产竹子,目前竹子数量:1消费者-F....消费竹子,目前竹子数量:0


本文定义了六个线程,生产线程A,B,C,消费线程D,E,F,六个线程都尝试去获取锁,当线程A成功获取锁时,其他线程都会阻塞在同步阻塞队列中,修改waitStatus状态为-1,同时park住线程。\

假设线程A先执行producerBamboo()方法,由于flag被volatile关键字锁修饰,固线程之间可见,由于flag是false,固不会将自己放入条件等待队列中,

竹子数量自增+1,设置volatile flag=true,则说明有竹子,尝试去唤醒条件等待队列(消费)中唤醒消费线程。

public void producerBamboo() {        lock.lock(); // 获取锁资源        try {            while (flag) { // 如果有竹子                try {                    producerCondition.await(); // 挂起生产竹子的线程                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            bambooCount++; // 竹子数量+1            System.out.println(Thread.currentThread().getName() + "....生产竹子,目前竹子数量:" + bambooCount);            flag = true; // 竹子余量状态改为true            consumerCondition.signal(); // 生产好竹子之后,唤醒消费竹子的线程        } finally {            lock.unlock(); // 释放锁资源        }}
  • 当条件等待队列(消费)中有线程时,是因为消费者先获取了锁,然后根据flag=flase,进入了while判断中,获取锁的消费线程调用了 consumerCondition.await()。把当前线程放入到条件等待队列中addConditionWaiter(),然后消费线程尝试释放锁,同时判断当前消费线程是否在同步阻塞队列中,如果不在,则说明可以阻塞。
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        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);


}
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}
  • 当条件等待线程(消费)中没有线程时,生产者尝试去调用doSignal(),如果为null,则什么都不会执行。
public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}

\

如果线程B获取到锁时,发现flag为true,则把自己构造成Node节点,放入条件等待队列中,同时释放锁,然后唤醒消费同步等待队列中的线程尝试去消费锁。这样就实现了生产者-消费者的demo了。