Java高并发22-AQS条件变量的支持

一、入队操作

  • 当一个线程获取锁失败之后会被转换为Node节点,然后会使用enq方法,将该节点插入到AQS的阻塞队列,下面看一下这个方法如何实现
	private Node enq(final Node node) {
		for(;;) {
			Node t = tail;
			if( t == null) {
				if(compareAndSetHead(new Node())) {
					tail = head;
				}
			}else {
				node.prev = t;
				if(compareAndSetTail(t,node)) {
					t.next = node;
				}
			}
		}
	}
  • 解析:进入到第一次循环,tail和head此时都是null,如图default,然后让t指向tail,即null,如图I,此时都是null,然后随便产生一个实例,也就是II所示,让head指向它,然后执行语句,让tail也指向它;
  • 接下来进行第二次循环,让t也指向这个实例,如图III,然后进入到else语句,先让node的前向指向哨兵,如图IV;然后,把tail设置为node,在把哨兵的next指向tail,如图VI,也就结束了。
    22.1

二、AQS-条件变量的支持

  • notify和wait方法是配合synchronized内置锁实现线程同步的,条件变量的signal和await方法也是用来配合锁(使用AQS实现的锁),实现线程间同步的基础设施
  • 它们的不同在于,synchronized同时只能与一个共享变量的notify或者wait方法实现同步,而AQS的一个锁可以对应多个条件变量,我们先看下面的例子
package com.ruigege.LockSourceAnalysis6;

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

public class TestReentrantLock {

	public static void main(String[] args) {
		ReentrantLock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		// Condition类是锁对应的一个条件变量,一个lock对象是可以创建多个条件变量的
		
		lock.lock(); // 首先获取独占锁
		try {
			System.out.println("begin wait");
			condition.await(); // 阻塞当前线程,当其他线程调用条件变量的signal方法的时候,被阻塞的线程就会这
			// 这里放回,需要注意的是和调用Object的wait方法一样,如果在没有获取锁之前调用了
			// 条件变量的await方法,就会抛出java.lang.IllegalMonitorStateException
			System.out.println("end wait");
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock(); // 释放了锁
		}
		
		lock.lock();
		
		try {
			System.out.println("begin signal");
			condition.signal();
			System.out.println("end signal");
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}
}

22.2

  • 其实这里的Lock对象就相当于synchronized加上了共享变量,调用lock.lock()就相当于进入了synchronized块(获取了共享变量的内置锁),调用lock.unlock()方法就相当于推出了synchronized块,调用条件变量的signal方法就相当于调用共享变量的notify方法,signalAll()相当于notifyAll()方法
  • 在上面的代码中,lock.newCondition()的作用其实就是new了一个在AQS内部声明的ConditionObject对象,ConditionObject是AQS的内部类,可以方法AQS内部的变量(例如状态变量state)和方法,在每个条件变量内部都维护了一个条件队列,用来存放调用条件变量的await()方法时被阻塞的线程,注意这个条件队列和AQS队列不是一回事。
  • 下面便是await方法源码,当线程调用条件变量的await()方法时(必须先调用锁的lock方法获取锁),在内部会构造一个类型为Node.CONDITION的node节点,然后将该节点插入到条件队列末尾,之后当前线程会释放获取得锁(也就是会操作锁对应的state变量的值),并被阻塞挂起,这时候如果有其他线程调用lock.lock()尝试获取锁,就会有一个线程获取到锁,如果获取到的锁的线程调用了条件变量的await方法,则该线程会被放入到条件变量的阻塞队列中,然后释放获取到的锁,在await方法处阻塞。
	public final void await() throws InterruptedException {
		if(Thread.interrupted()) {
			throw new InterruptedException();
		}
		// 创建新的Node节点,并且插入到条件队列末尾
		Node node = addContionWaiter();
		// 释放当前线程获取到的锁
		int savedState = fullyRelease(node);
		int interruptMode = 0;
		// 调用park方法阻塞挂起当前线程
		while(!isOnSyncQueue(node)) {
			LockSupport.park(this);
			if((interruprMode = checkInterruptWhileWaiting(node)) != 0) {
				break;
			}
		}
	}

三、源码:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值