Condition
1.5诞生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();
}
核心两个方法:await和signal.
其实类似于Object对象中的wait和notify。类比synchronized和Lock,可以看出JUC对于原有的方法的修改和提升。具体实现细节,如下慢慢分析。
AQS的实现
基本其他的JUC的同步共享锁实现类或多或少的调用了该实现,所以直接分析AQS实现类。
基本属性
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
从源码里面可以看出,condition是实现了一个单向链表(仅仅维护nextWaiter)。
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);
}
时序图~
- AddConditionWaiter
将当前Node放入到Condition单向链表当中,初始化firstWaiter和lastWaiter
- fullyRelease
释放当前锁,因为可能是重入,所以要release(state)并且保存state
- isOnSyncQueue
循环判断是否是在同步队列,如果不是,则park这个线程
- acquireQueued
当park的线程被唤醒,也就是他已经在同步队列中了。随后立刻尝试获取锁,同时获取state的重入。(当然也可能获取不到,排在同步队列当中)
- …
随后是清理Cancel状态的节点,同时如果有异常打印异常。
signal方法
AQS源码:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
- isHeldExeclusively方法
看看是不是自己唤醒自己…
- doSignal方法
按照顺序从firstWaiter开始找到第一个Condition状态的节点,更新状态并且放入到同步队列单中。
signalAll方法
AQS源码:
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
- isHeldExeclusively方法
看看是不是自己唤醒自己…
- doSignalAll方法
按照顺序从firstWaiter开始更新状态并且放入到同步队列单中。
总结
-
JUC的await和signal类似于Object类中的wait和notify。个人感觉只是wait和notify是native的,是在cpp代码里面实现,而await和signal是在java代码里面实现,并且提供更多的功能。
-
每一个lock都可以有多个condition,同时也就拥有了多个单向链表
public Condition newCondition() { return sync.newCondition(); } final ConditionObject newCondition() { return new ConditionObject(); }
JUC的同步工具
简单介绍下CyclicBarrier, CountDownLatch, Semaphore。
CountDownLatch
内部维护了的sync类实现了AQS的共享锁,重新复写了获取和释放共享锁的方法。源码如下:
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
- 共享锁不像独享锁,仅仅关心state是否为0.
- 初始化时候设置state,每次countDown都state都-1.
用法Demo
public class CountDownLatchDemo {
/*
发令枪,所有都准备好,发令枪一开始全部运行
*/
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++){
executorService.execute(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t finish!");
});
}
System.out.println("Main Thread finish!");
TimeUnit.SECONDS.sleep(1);
countDownLatch.countDown();
executorService.shutdown();
}
}
public class CountDownLatchDemo2 {
/*
计数器,当前面任务完成了~在执行后面任务
*/
private static CountDownLatch countDownLatch = new CountDownLatch(10);
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++){
executorService.execute(()->{
System.out.println(Thread.currentThread().getName() + "\t finish!");
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("Main Thread finish!");
executorService.shutdown();
}
}
CyclicBarrier
类似于CountDownLatch,当屏障经历过后~又会有一个新的一样的屏障。
- 实现方法 没有用到AQS(可能是因为比较简单把…)直接用Lock和Condition实现。
- 核心方法await 源码如下:
/**
* Main barrier code, covering the various policies.
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
代码流程 :
- 锁住资源,然后进行状态和参数校验
- 减少数量,如果减少没有到0,则await。
- 如果减少到0了,则执行runnable,重新更新generation。Runnable仅仅执行run方法,而非开新线程
用法demo
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CycleBarrierDemo {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
int travellers = 3;
CyclicBarrier guide = new CyclicBarrier(travellers, new Guide());
for (int i = 1; i < 4; i++) {
System.out.println(String.format("************第%d天****************", i));
new Thread(new Travller("eric", guide,1000L)).start();
new Thread(new Travller("robin", guide,2000L)).start();
new Thread(new Travller("edward", guide,3000L)).start();
Thread.sleep(4000L);
}
}
static class Guide implements Runnable{
@Override
public void run() {
System.out.println("collect passport!!!");
}
}
static class Travller implements Runnable{
private String name;
private CyclicBarrier guide;
private long lateTime;
Travller(String name, CyclicBarrier guide, long lateTime){
this.name = name;
this.guide = guide;
this.lateTime = lateTime;
}
@Override
public void run() {
try{
System.out.println(name + ":\t late:" + lateTime);
Thread.sleep(lateTime);
guide.await();
System.out.println(name + "\t begin to travel!");
}catch (Exception e){
}
}
}
}
三个游客,一个导游,三天。
- 一个游客一个线程,迟到的时间不同,被导游收集完护照可以旅行
- 一个导游负责游客都倒了,收集护照
- 三天用来表现导游可以被复用(new Generation)
Semaphore
中文翻译:信号。
内部私有静态类Sync实现了AQS,同时内部也实现了公平锁和非公平锁。
公平锁和非公平锁区别:
-
对于tryAccquireShared方法的实现不同
-
公平锁 通过CAS循环来减少state,但是每次循环之前先去判断是否在同步Queue里面有前辈。如果有则返回。源码:
static final class FairSync extends Sync { private static final long serialVersionUID = 2014338818796000944L; FairSync(int permits) { super(permits); } protected int tryAcquireShared(int acquires) { for (;;) { if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } }
-
非公平锁 通过CAS循环来减少state。源码:
final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } }
-
用法Demo
可以理解为停车场一共就有那么多个车位(),只有车走了,才能有车在停进来,如果没有的话,就一直hang在那里
package juc;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
static Random random = new Random();
public static void main(String[] args) {
Semaphore parkingLot = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(new ParkingCar(parkingLot, ("car"+i))).start();
}
}
private static class ParkingCar implements Runnable{
private Semaphore parkingLot;
private String carName;
ParkingCar(Semaphore parkingLot, String carName){
this.parkingLot = parkingLot;
this.carName = carName;
}
@Override
public void run() {
try {
//等待停车场有车位,有则直接进,没有则hang住,进入同步队列等待,直到有车走了(释放了同步锁)
//公平锁和非公平锁区别就在这里,如果有车提前排队了,那么公平锁就会让前面的车先进去,非公平锁就是同时争抢,谁先cas成功算谁的
parkingLot.acquire();
System.out.println(carName + "\tis entering!!!");
int parkTime = random.nextInt(5);
TimeUnit.SECONDS.sleep(parkTime);
System.out.println(String.format("%s\tis leaving after parking %d second******", carName, parkTime));
parkingLot.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
个人笔记。如有错误还请帮忙指出,多谢~