1. JUC核心之AQS
1.1 重入锁ReentrantLock的初步认识
锁是用来解决多线程并发访问共享资源所带来的数据安全性问题的手段,对一个共享资源加锁后,如果有一个线程获得了锁,那么其他线程无法访问这个共享资源
加锁前后的区别
通过查看jdk源码目录rt.jar->java->util->concurrent->locks
可以看到lock接口的很多子类和实现,我们先使用ReentrantLock
public class ReenDemoTest {
static Lock lock = new ReentrantLock();
public static int i = 0;
public static void inc(){
try {
lock.lock();//获得锁
Thread.sleep(1);
i++;
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
}
public static void main(String[] args) throws InterruptedException {
for (int i1 = 0; i1 < 1000; i1++) {
new Thread(ReenDemoTest::inc).start();
}
Thread.sleep(4000);
System.out.println(i);
}
}
运行结果一直都是1000
1.2 什么是重入锁
一个持有锁的线程,在释放所之前,如果再次访问加了该同步锁的其他方法,这个线程不需要再次争抢锁,只需要记录重入次数
public static void inc(){
try {
lock.lock();//获得锁
Thread.sleep(1);
i++;
decree();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
}
//递减
public static void decree(){
lock.lock();//获得锁 当线程A进来的时候,等待线程A释放锁(这回导致死锁,引入重入锁解决)
i--;
lock.unlock();//释放锁
}
2.AQS是什么
2.1 线程的排队,阻塞
ReentrantLock的类的关系图
源码分析:
分析ReentrantLock的构造方法
public ReentrantLock() {
sync = new NonfairSync();
}
我们可以看到ReentrantLock在初始化时初始化了一个非公平锁对象
public void lock() { sync.lock();}
lock.lock();//获得锁
从类图的结构我们可以看到,ReentrantLock.lock方法主要使用的是AQS(AbstractQueuedSynchronizer后续都用AQS说明)
进入ReentrantLock的lock方法
private volatile int state;//一个被volatile修饰的共享资源,用于判断是否有线程获得了锁,0为没有,1为有
final void lock() {
if (compareAndSetState(0, 1)) //判断当前线程暂用所是否成功,然后将该线程设置成独占锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//当线程不满足占有条件执行如下代码
}
图解:
此时线程B,C进入是如何的状态,查看源码分析
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获取锁,如果当前锁被线程占用,则返回失败
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//失败的线程构建一个 thread 和waitStates为元素的node节点为基本单元的双向链表
selfInterrupt();
}
查看addWaiter方法
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;//將线程对象和等待状态封装程一个node节点返回
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
查看acquiredQueued方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//死循环
final Node p = node.predecessor();//前一个节点
if (p == head && tryAcquire(arg)) {//判断前一个接点是否是头结点,并且尝试获取锁,如果获取到,表示上一个线程执行结束,当前线程抢占到锁,这是修改双向链表的数据结构使得头一个节点从链表中移除
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&//判断是否应该阻塞线程
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在Lock.lock()方法中通过调用AQS的如上方法实现了线程的排队和阻塞
2.2 线程的唤醒
在Lock.unlock()中,查看源码分析
lock.unlock();//释放锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {//释放资源
Node h = head;
if (h != null && h.waitStatus != 0)//判断头节点(也就是运行的线程是否执行结束)
unparkSuccessor(h);
return true;
}
return false;
}
查看unparkSuccessor方法
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;//判断当前节点状态
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);//CAS设置状态
//以下操作实现将队列中的下一个节点中的线程唤醒
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//唤醒节点中的线程
}
图解:
3.深入浅出CountDownLatch
3.1 CountDownLatch是什么
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,知道其他线程的操作完毕再执行,从命名可以解读countdown类似倒计时
实例:
public class CountDownLatchTest {
private static CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
countDownLatch.countDown();
}).start();
new Thread(()->{
countDownLatch.countDown();
}).start();
countDownLatch.await();
System.out.println("Thread end");
}
}
当我执行上面这段代码的时候,永远不会执行结束,如果在此基础上在加一个子线程
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
countDownLatch.countDown(); //倒计时3-1
}).start();
new Thread(()->{
countDownLatch.countDown(); //倒计时2-1
}).start();
new Thread(()->{
countDownLatch.countDown(); //倒计时1-1
}).start();
countDownLatch.await(); //只有当子线程都执行完,await方法的阻塞才会唤醒
System.out.println("Thread end");
}
执行结果为:Thread end,类似倒计时的作用
public class CountDownLatchDemo implements Runnable{
static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(new CountDownLatchDemo()).start();
}
}
@Override
public void run() {
try {
countDownLatch.await();//阻塞
//TODO
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
注:用于模拟1000个线程并发访问的情况
3.2 CountDownLatch源码分析
查看
countDownLatch.countDown(); 和countDownLatch.await();核心是使用共享锁机制,实现传递唤醒后续的线程
查看countDownLatch.countDown(); 方法
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head)//传递唤醒下一个节点 // loop if head changed
break;
}
查看countDownLatch.await(); 方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//尝试获取共享锁
doAcquireSharedInterruptibly(arg);
}
查看doAcquireSharedInterruptibly方法
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);//设置共享锁节点
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);//循环获取下一个节点并设置状态
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&//对于异常节点处理
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
查看setHeadAndPropagate方法
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
图解:
4. Semaphore
4.1 什么是Semaphore
Semaphore也就是我们常说的信号灯,Semaphore可以控制同时访问的线程个数,通过acquire获取一个许可,如果没有就等待,通过release释放一个许可,类似限流的作用
实例:写一段代码实现银行多个窗口同时业务办理
public class BankDemo {
// 最大支持5个窗口同时办理业务
public static Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Customer(i,semaphore).start();
}
}
public static class Customer extends Thread{
private int num;
private Semaphore semaphore;
public Customer(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();//如果获取不到许可,就会被阻塞
System.out.println("第" + num + "个客户开始办理业务" );
Thread.sleep(200);//模拟办理业务时间
System.out.println("第" + num + "个客户业务办理完成" );
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果如下:
4.2 semaphore源码分析
查看他的类图
可以看到semaphore使用了aqs的部分实现逻辑
查看他的构造方法
public static Semaphore semaphore = new Semaphore(5);
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
NonfairSync(int permits) {
super(permits);
}
Sync(int permits) {
setState(permits);
}
protected final void setState(int newState) {
state = newState;
}
可以看到最后调用了AQS的setstate方法,将初始化个数设置为state的值
查看semaphore.acquire方法
semaphore.acquire();
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) //调用AQS的获取锁方法
doAcquireSharedInterruptibly(arg);
}
后续代码调用AQS获取非公平锁的方式,并将未运行的线程构建一个双向链表排队,阻塞
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
继续查看semaphore.release方法
semaphore.release();
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//调用AQS的释放锁方法
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
//释放锁并获取下一个节点的线程,获取一个signal类型的非共享锁
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
5.CyclicBarrier的基本应用
5.1 什么是CyclicBarrier
CyclicBarrier的字面意思是可循环使用的屏障,他要做的事情是,让一组线程到达一个屏障(也可以叫做同步点)时被阻塞,知道最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作
CycliBarriedDemo类
public class CycliBarrierDemo extends Thread{
@Override
public void run() {
System.out.println("所有子线程执行完成,执行主线程");
}
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new CycliBarrierDemo());
new CycliBarrierTest("path1",cyclicBarrier).start();
new CycliBarrierTest("path2",cyclicBarrier).start();
new CycliBarrierTest("path3",cyclicBarrier).start();
}
}
CycliBarrierTest类
public class CycliBarrierTest extends Thread{
private String path;
private CyclicBarrier cyclicBarrier;
public CycliBarrierTest(String path,CyclicBarrier cyclicBarrier) {
this.path = path;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("开始上传路径" + path + "下的文件");
}
}
执行结果如下:所有子线程执行结束,但是主线程并没有执行
修改CycliBarrierTest代码,修改run方法
@Override
public void run() {
System.out.println("开始上传路径" + path + "下的文件");
try {
cyclicBarrier.await();//使子线程陷入阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
此时当所有子线程执行结束之后,主线程也执行了
CycliBarrier的使用场景
当存在需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用cyclicBarrier
注:1.parties,是传入的构造方法的参数,如果因为某种原因导致没有足够多的线程来调用await方法,这个时候会导致所有线程都被阻塞
2.await(timeout,unit)设置一个超时等待时间
3.reset重置计数会产生brokenBarrierException
5.2 CyclicBarriery实现原理
源码分析,查看await方法
cyclicBarrier.await();
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);//调用等待方法
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
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)//判断是否是broken状态
throw new BrokenBarrierException();
if (Thread.interrupted()) { //判断线程是否中断状态
breakBarrier();
throw new InterruptedException();
}
int index = --count; //这里的count等于states也就是初始化的子线程的个数
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();
}
}
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 {
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();
}
}
查看nextGeneration方法
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();//唤醒所有等待的线程
// set up next generation
count = parties;
generation = new Generation();
}
图解:
6.condition条件
什么是condition
condition是一个多线程协通信的工具类,可以让某些线程一起等待某个条件(condition),只有满足条件时,线程才会被唤醒
实例:
ConditionDemoWait类
public class ConditionDemoWait extends Thread{
private Lock lock;
private Condition condition;
public ConditionDemoWait(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin- ConditionDemoWait");
try {
lock.lock();
condition.await();
System.out.println("end- ConditionDemoWait");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
ConditionDemoNotify类
public class ConditionDemoNotify extends Thread{
private Lock lock;
private Condition condition;
public ConditionDemoNotify(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin- ConditionDemoNotify");
try {
lock.lock();
condition.signal();
System.out.println("end- ConditionDemoNotify");
}finally {
lock.unlock();
}
}
}
测试类
public class ConditionDemo {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
ConditionDemoWait conditionDemoWait = new ConditionDemoWait(lock,condition);
ConditionDemoNotify conditionDemoNotify = new ConditionDemoNotify(lock,condition);
conditionDemoWait.start();
conditionDemoNotify.start();
}
}
注意,构造方法这里需要传入同一个lock和condition
执行结果如下:
查看源码分析,先查看wait类的方法
lock.lock方法会构造一个线程队列,继续执行
condition.await();
进入condition的实现类AQS中查看
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); //构建了一个condition的等待队列
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方法
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;
}
查看condition.signal
condition.signal();
调用的AQS的signal方法
public final void signal() {
if (!isHeldExclusively()) //异常判断
throw new IllegalMonitorStateException();
Node first = firstWaiter; //将获取condition队列第一个节点
if (first != null)
doSignal(first);
}
private void doSignal(Node first) { //
do {
if ( (firstWaiter = first.nextWaiter) == null) //将下一个节点位置的值复制给第一个节点,并判断是否为空,将第一个节点移除
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
transferForSignal方法
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node); //将codition中第一个节点挪到lock线程队列中,进入等待状态
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread); //唤醒休眠的线程
return true;
}
图解 分析: