多线程(十一、AQS原理-ReentrantLock的条件队列Condition)

1、Condition介绍

1.1 Condition是对线程的wait,notify的增强

1.2 在ReentrantLock中他的实现类是AQS中的ConditionObject,实现了Condition接口,利用AQS的节点,实现了条件队列。

2、案例分析

2.1 说明:Thread-1获取锁,然后await,释放锁;Thread-2获得锁,唤醒Thread-1,释放锁;Thread-1重新获取锁,释放锁。

2.2 代码

2.2.1 Thread-1

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

public class Task1 implements Runnable{

    private ReentrantLock lock;
    private Condition con;

    public Task1(ReentrantLock lock, Condition con) {
        this.lock = lock;
        this.con = con;
    }

    @Override
    public void run() {

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "获取到锁....");
            System.out.println(Thread.currentThread().getName() + "开始阻塞....");
            con.await();
            System.out.println(Thread.currentThread().getName() + "重新获取锁,继续执行....");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "释放锁....");
            lock.unlock();
        }
    }
}

2.2.2 Thread-2

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

public class Task2 implements Runnable{

    private ReentrantLock lock;
    private Condition con;

    public Task2(ReentrantLock lock, Condition con) {
        this.lock = lock;
        this.con = con;
    }

    @Override
    public void run() {

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "获取到锁....");
            System.out.println(Thread.currentThread().getName() + "唤醒Thread-1....");
            con.signal();
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "释放锁....");
            lock.unlock();
        }
    }
}

2.2.3 启动文件

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

public class Main {

    public static void main(String[] args) throws ParseException, InterruptedException {
        ReentrantLock lock = new ReentrantLock(true);
        Condition condition = lock.newCondition();
        Thread t1 = new Thread(new Task1(lock,condition),"Thread-1");
        Thread.sleep(2000);
        Thread t2 = new Thread(new Task2(lock,condition),"Thread-2");
        t1.start();
        t2.start();
    }
}

2.2.4 运行结果

3、源码分析

3.1 获取锁的源码在上一篇已经分析过了,来看await操作:

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);
    }

3.1.1 await()方法会释放当前线程持有的锁,就是fullyRelease方法的作用

3.1.2 线程A释放了锁,并进入条件对列,处于阻塞状态。

3.2 Thread-2获取到锁后,调用signal方法唤醒Thread-1


final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        //将节点的状态修改为0,从【条件队列】节点状态转换为【等待队列】默认节点状态0,节点可以插入【等待队列】
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);//将节点插入【等待队列】
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) //将前驱节点设置为可唤醒状态
            LockSupport.unpark(node.thread);//唤醒节点,也就是Thread-1
        return true;
    }

3.3 Thread-2释放锁

3.3.1Thread-2释放锁,会唤醒【等待队列】的首节点,参看上一篇介绍(unparkSuccessor方法)

3.3.2 Thread-1继续执行

原文地址:https://blog.51cto.com/janephp/2410699

时间: 2024-09-29 16:09:36

多线程(十一、AQS原理-ReentrantLock的条件队列Condition)的相关文章

经典笔试题:线程通信(使用重入锁(ReentrantLock)和条件队列(Condition)实现线程间通信)

经典笔试题: 1.自定义容器,提供新增元素(add)和获取元素数量(size)方法.2.启动两个线程.线程1向容器中新增10个数据.线程2监听容器元素数量,当容器元素数量为5时,线程2输出信息并终止. package com.gaopeng.programming.test2; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.conc

java ReentrantLock结合条件队列 实现生产者-消费者模式

1 package reentrantlock; 2 3 import java.util.ArrayList; 4 5 public class ProviderAndConsumerTest { 6 7 static ProviderAndConsumer providerAndConsumer = new ProviderAndConsumer(); 8 9 public static void main(String[] args) throws InterruptedException

多线程(十、AQS原理-ReentrantLock实现)

ReentrantLock介绍 ReentrantLock 基于AQS实现了公平和非公平的独占锁功能. ReentrantLock定义AQS的同步状态(synchronization state)如下: State为0表示锁可用:为1表示被占用:为N表示锁重入的次数,是独占资源. ReentrantLock实现公平锁原理 案例代码如下: 1.启动文件 public class Main { public static void main(String[] args) throws ParseEx

Java并发:线程间同步-条件队列和同步工具类

转载请注明出处: jiq?钦's technical Blog - 季义钦 线程之间的同步,除了互斥(前面介绍的互斥锁)之外,还存在协作关系,下面我们就介绍一下java线程间常见的一些协作方式. 一.内置条件队列 正如每个Java对象都可以作为一个内置锁,每个对象也可以作为一个条件队列,称为内置条件队列,Object.wait().notify()/notifyAll()构成了内置条件队列的API. 需要注意的是,调用任何对象X的内置条件队列的API都必须要先获得该对象X的内置锁. 1.API介

AQS 原理以及 AQS 同步组件总结

1 AQS 简单介绍 AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面. AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLock,Semaphore,其他的诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask 等等皆是基于 AQS 的.当然,我们自己也能利用 AQ

java架构之路(多线程)AQS之ReetrantLock显示锁的使用和底层源码解读

锁的粗化和锁的消除 这个本来应该是在synchronized里面去说的,忘记了,不是很重要,但是需要知道有这么一个东西啦. 我们先来演示一下锁的粗化: StringBuffer sb = new StringBuffer(); public void lockCoarseningMethod(){ //jvm的优化,锁的粗化 sb.append("1"); sb.append("2"); sb.append("3"); sb.append(&qu

AQS原理分析

一,AQS原理 lock最常用的类就是ReentrantLock,其底层实现使用的是AbstractQueuedSynchronizer(AQS) 简单来说AQS会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全部处于阻塞状态,经过调查线程的显式阻塞是通过调用LockSupport.park()完成,而LockSupport.park()则调用sun.misc.Unsafe.park(

java并发-使用内置条件队列实现简单的有界缓存

内置锁和内置条件队列一起,一个简单的应用是创建可阻塞的有界缓存区,java并发包的BlockingQueue就是一个利用Lock和显式条件队列实现的可阻塞的有界队列.总结内置锁和内置条件的原理,这里我们用另一种方式实现简单的可阻塞缓存.源码如下: 首先,创建一抽象有界缓存类ABoundedBuffer,提供插入和删除的基本实现. /** * @title :ABoundedBuffer * @description :有界缓存抽象类 * @update :2014-12-30 上午9:29:33

AQS原理浅析

锁是最常用的同步方法之一,在高并发的环境下激烈的锁竞争会导致程序的性能下降,所以我们自然有必要深入的学习一下锁的相关知识. java的内置锁一直都是备受争议的,在JDK 1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6后,进行大量的锁优化策略,如自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等,但是与Lock相比synchronized还是存在一些缺陷的:虽然synchronized提供了便捷性的隐式获取锁释放锁机制(基于JVM机制),但是它却缺少了获取锁与释