スレッドプールシリーズ:
- 【JUCソースコード】スレッドプール:ThreadPoolExecutor(1)継承関係分析
- [JUCソースコード]スレッドプール:ThreadPoolExecutor(2)の基礎となる構造解析
- [JUCソースコード]スレッドプール:ThreadPoolExecutor(3)ワーカーデザインのアイデアとソースコード分析
- [JUCソースコード]スレッドプール:タスク実行プロセスのThreadPoolExecutor(4)ソースコード分析
- [JUCソースコード]スレッドプール:ThreadPoolExecutor(5)の概要
- [JUCソースコード]スレッドプール:スレッドプール&Excutorsを作成するためのパラメーター設定のアイデア
- [JUCソースコード]スレッドプール:ThreadPoolに関するいくつかの質問
労働者のデザインのアイデア
理論的には、スレッドプールは多くのスレッドを維持することであり、各スレッドは多くのタスクを持つことができます。しかし、ここに問題があります。スレッドを作成するときに特定のRunnableを直接渡すと、次のようになります。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}).start();
その後、このスレッドの実行後にhelloを出力するタスクは終了し、スレッドを再利用して複数のタスクを実行することはできません。したがって、スレッドを作成するときに特定のタスクを配置するのではなく、Runnableを再度カプセル化し、次のように、runメソッドでタスクを取得するための特定のメソッドを呼び出す必要があります。
public class Work implements Runnable {
@Override
public void run() {
// runwork 方法才是真正执行任务的方法
runwork();
}
}
main() {
// 创建线程时传入 work,调用逻辑就是 Work#run() -> runwork() -> 具体Runnable的run()
// 所以,只要保证 runwork 方法能一直获取任务,则该线程就能一直运行
// 另外,在 runwork 获取任务时,还可以扩展线程回收策略,因为只要该方法返回了,该线程就该结束了
new Thread(Work).start();
}
私が上で言ったことを理解した後、ThreadPoolExecutorのコアロジックは実際にはほとんど明確です。
ワーカーソースコード分析
スレッドプール内のタスク実行の最小単位。
ThreadPoolExecutorのワーカーは、私が上で述べたものよりも独創的です。これは、Runnableを再カプセル化することを目的としておらず、Runnableインターフェイスを実装しながらスレッドをカプセル化することを目的としているため、スレッドの再利用(つまり、新しいThread(this))の目的を達成します。
さらに、Workerはスレッドの作成時に最初のタスクfirstTaskも保存しますが、firstTaskの実行後、nullに設定され、getTask()を介してタスクキューから新しいタスクが継続的に取得されて実行されます。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// 执行当前 worker 的线程
final Thread thread;
// 创建该线程时的任务(第一个任务),执行完后=null
Runnable firstTask;
// 在创建 worker 的时候创建 Thread
Worker(Runnable firstTask) {
// 将AQS的状态设置为-1
// 从后面的isLocked方法可以看到,state!=0 表示已经被加锁
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
// !!!为了线程的复用,Worker本身实现了 Runnable,并且把自己作为任务传递给 thread。非常巧妙的设计!
this.thread = getThreadFactory().newThread(this);
}
// 调用逻辑:Woker#run -> runWorker() -> 具体Runnable的run()
// runWorker 获取任务的方式有二:
// 1.firstTask,执行完就置为 null
// 2.getTask() 不断从从阻塞队列中获取
public void run() {
runWorker(this);
}
}
PS:作成されたスレッドが多すぎて、タスクなしでアイドル状態になっているスレッドが多い場合はどうなりますか?
回答:スレッドが遅すぎてタスクの実行を待つことができない場合、スレッドはリサイクルされます。特定のリサイクル戦略は、getTask()メソッドで確認できます。
さらに、ワーカー自体もAQSを実装しているため、ロックでもあります。タスクの実行時に自身をロックします。タスクが完了すると、ワーカー自体が解放され、スレッドが実行されたときに別のスレッドにスローされるようになります。タスク。タスク。関連する方法は次のとおりです。
public void lock() {
acquire(1); }
public boolean tryLock() {
return tryAcquire(1); }
public void unlock() {
release(1); }
public boolean isLocked() {
return isHeldExclusively(); // Lock methods
// 尝试加锁,CAS 赋值为 1,表示锁住
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 尝试释放锁,释放锁没有 CAS 校验,可以任意的释放锁
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 0 代表没有锁住,否则代表锁住(-1,1)
protected boolean isHeldExclusively() {
return getState() != 0;
}
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}