java-线程池

什么是线程池

线程池 = worker线程 + 任务队列

worker线程

继承自AQS,并且实现了Runnable接口。worker线程不断从任务队列里获取任务并执行,当任务队列里面没有任务可执行了或者任务执行途中抛出异常时,线程退出,销毁线程。

AQS

AQS为那些基于FIFO等待队列的同步器和阻塞锁提供了一个框架
AQS为那些用单个原子int值表示状态的同步器,提供了状态的原子操作(getState、setState、compareAndSetState)
同步器要继承自AQS,并作为非public的内部helper类,具体的public同步方法由外围包装类提供
AQS支持互斥、共享两种获取锁的模式,子类同步器一般实现其中一种模式,也可以实现两种模式供用户选择,如ReadWriteLock
AQS定义了嵌套类ConditionObject 实现了Condition接口,支持互斥模式的子类同步器可以使用此对象来实现Object内置的wait-notify语义
子类同步器需要实现以下方法,且要保证实现方法的线程安全,修改或者获取状态使用AQS提供的方法getState、setState、compareAndSetState
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
isHeldExclusively

AQS内部的FIFO队列的使用方式如下:

1
2
3
4
5
6
7
8
9
Acquire:
while (!tryAcquire(arg)) { //尝试获取锁
enqueue thread if it is not already queued; //获取失败则,将当前线程入队列
possibly block current thread; //当前线程在FIFO队列阻塞等待
}

Release:
if (tryRelease(arg)) //尝试释放锁
unblock the first queued thread; //释放成功则,将FIFO队首线程出队列

如果AQS不能满足你的同步器需求,你可以基于更底层的java.util.concurrent.atomic类、java.util.Queue类和LockSupport类来实现自定义的同步器

AQS使用举例:
Mutex类:互斥锁,状态0代表unlock,状态1代表locked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Mutex implements Lock, java.io.Serializable {

// 内部Helper类继承自 AQS
private static class Sync extends AbstractQueuedSynchronizer {
// 查看锁是否已被占用
protected boolean isHeldExclusively() {
return getState() == 1;
}

// 获取锁,将state由 0 修改为 1
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}

// 释放锁,将state由 1 修改为 0
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}

// 提供Conditon接口实现等待通知机制
Condition newCondition() { return new ConditionObject(); }

// Deserializes properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}

// 属性实例化同步器(初始化线程安全),并将同步器置为final(操作线程安全)
private final Sync sync = new Sync();

// 外围包装类Mutex提供用户使用接口
public void lock() { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}

CLH队列:基于链表的确保无饥饿的、提供先来先服务的,公平的自旋锁,申请线程只在本地变量上自旋,它【不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋,获得锁】
当一个线程获取锁时:
1. 创建Node节点:封装线程+状态,状态置为:获取锁, 加入FIFO队列队尾
2. 获取队列里的前驱节点,自旋检查前驱节点的状态,直到前驱节点状态为:释放锁 时,自己获取到锁,并回收前驱节点

当一个线程释放锁时:
1. 释放锁的节点必然是队列头节点,设置自己的状态为:释放锁

等待队列:CLH队列的变种 用于阻塞同步
当一个线程获取锁时: 没获取到,会加入队尾,并且使用LockSupport.park 阻塞当前线程,进入睡眠状态

当上一个节点释放锁时,使用LockSupport.unpark 去通知后继节点,唤醒相应的线程

任务队列

任务队列实现BlockingQueue接口,细分为:

无界队列:DelayedWorkQueue、LinkedBlockingQueue

有界队列:ArrayBlockingQueue

直接递交给worker线程型队列(队列不存放任务):SynchronousQueue

线程池中的位运算

-------------本文结束感谢您的阅读-------------
Good for you!