1. スレッド
1. スレッドとは何ですか?
スレッドは、オペレーティング システムが計算スケジュールを実行できる最小単位です。これはプロセスに含まれており、プロセス内の実際の操作単位となります。スレッドとは、プロセス内の単一の順次制御フローを指します。プロセス内で複数のスレッドを同時に実行でき、各スレッドは異なるタスクを並行して実行します。
2. スレッドの立て方
2.1. JAVA でのスレッドの作成
/**
* 继承Thread类,重写run方法
*/
class MyThread extends Thread {
@Override
public void run() {
System.out.println("myThread..." + Thread.currentThread().getName());
} }
/**
* 实现Runnable接口,实现run方法
*/
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable..." + Thread.currentThread().getName());
} }
/**
* 实现Callable接口,指定返回类型,实现call方法
*/
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "MyCallable..." + Thread.currentThread().getName();
} }
2.2. テストする
public static void main(String[] args) throws Exception {
MyThread thread = new MyThread();
thread.run(); //myThread...main
thread.start(); //myThread...Thread-0
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
myRunnable.run(); //MyRunnable...main
thread1.start(); //MyRunnable...Thread-1
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread thread2 = new Thread(futureTask);
thread2.start();
System.out.println(myCallable.call()); //MyCallable...main
System.out.println(futureTask.get()); //MyCallable...Thread-2
}
2.3. 問題点
スレッドを作成したのに、メソッドを直接呼び出したときと start() メソッドを呼び出したときの結果が異なるのはなぜですか? new Thread() は実際にスレッドを作成しますか?
2.4. 問題分析
メソッドを直接呼び出すと、それが実行のメイン スレッドであることがわかり、start() メソッドを呼び出すと新しいスレッドが開始されます。これは、new Thread() がスレッドを作成するのではなく、start でスレッドを作成することを意味します。 ()。
次に、Thread クラスの start() メソッドを見てみましょう。
class Thread implements Runnable { //Thread类实现了Runnalbe接口,实现了run()方法
private Runnable target;
public synchronized void start() {
...
boolean started = false;
try {
start0(); //可以看到,start()方法真实的调用时start0()方法
started = true;
} finally {
...
}
}
private native void start0(); //start0()是一个native方法,由JVM调用底层操作系统,开启一个线程,由操作系统过统一调度
@Override
public void run() {
if (target != null) {
target.run(); //操作系统在执行新开启的线程时,回调Runnable接口的run()方法,执行我们预设的线程任务
}
}
}
2.5. 概要
2. マルチスレッド
1. マルチスレッドとは何ですか?
マルチスレッドとは、ソフトウェアまたはハードウェアからの複数のスレッドの同時実行を可能にするテクノロジーを指します。同じスレッドは、次のタスクを処理する前に 1 つのタスクのみを処理できます。場合によっては、複数のタスクを同時に処理する必要がある場合があります。この場合、タスクを同時に処理するために複数のスレッドを作成する必要があります。
2. マルチスレッドの利点は何ですか?
2.1. シリアル処理
public static void main(String[] args) throws Exception {
System.out.println("start...");
long start = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
Thread.sleep(2000); //每个任务执行2秒
System.out.println("task done..."); //处理执行结果
}
long end = System.currentTimeMillis();
System.out.println("end...,time = " + (end - start));
}
//执行结果
start...
task done...
task done...
task done...
task done...
task done... end...,time = 10043
2.2. 並列処理
public static void main(String[] args) throws Exception {
System.out.println("start...");
long start = System.currentTimeMillis();
List<Future> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(2000); //每个任务执行2秒
return "task done...";
}
};
FutureTask task = new FutureTask(callable);
list.add(task);
new Thread(task).start();
}
list.forEach(future -> {
try {
System.out.println(future.get()); //处理执行结果 } catch (Exception e) {
}
});
long end = System.currentTimeMillis();
System.out.println("end...,time = " + (end - start));
}
//执行结果
start...
task done...
task done...
task done...
task done...
task done... end...,time = 2005
2.3. 概要
2.4. マルチスレッドの問題
上記の例では、タスクごとにスレッドを作成すると、タスクが多数ある場合に大量のスレッドが作成され、システム リソースの枯渇につながる可能性があることがわかります。同時に、スレッドの実行には CPU リソースが占有される必要があることがわかっており、スレッドが多すぎると、スレッド切り替えのオーバーヘッドに多くの時間が費やされます。
さらに、各タスクにはスレッドの作成が必要であり、スレッドの作成にはオペレーティング システムの基礎となるメソッドを呼び出す必要がありますが、これにはコストがかかり、スレッドは完了後にリサイクルされます。多数のスレッドが必要な場合、スレッドの作成に時間がかかります。
3. スレッドプール
1. スレッドプールの設計方法
マルチスレッド開発には上記の問題がいくつかあるため、これらの問題を回避するために何かを設計することはできますか? もちろんです! スレッド プールはこれらの問題を解決するために生まれました。では、これらの問題を解決するにはスレッドプールをどのように設計すればよいのでしょうか?つまり、スレッドプールにはどのような機能があればよいのでしょうか?
1.1. スレッドプールの基本機能
1.2. スレッドプールが問題に直面している
1.3. イノベーションは生活から生まれる
まずシナリオを想定してみましょう: 私たちが物流会社の経営者であるとします。配送する商品は私たちの仕事であり、トラックは配送ツールです。もちろん、商品の数だけトラックを用意することはできません。では、顧客が私たちに商品を届け続けた場合、会社が最善の経営を行うためには、どのように管理すればよいのでしょうか?
1.4. テクノロジーはイノベーションから生まれる
上記のシナリオに基づくと、物流会社はスレッド プール、商品はスレッド タスク、トラックはスレッドとなります。会社のトラック管理プロセスを設計する方法は、スレッド プール管理スレッドのプロセスを設計する方法と同じである必要があります。
2. スレッドプールの詳細分析
上でスレッド プールの設計方法について説明しましたが、次にマスターがスレッド プールをどのように設計したかを見てみましょう。
2.1. JAVA のスレッド プールはどのように設計されていますか?
2.1.1. スレッドプールの設計
スレッド プールの設計を理解するには、スレッド プールのプロパティを見てください。
public class ThreadPoolExecutor extends AbstractExecutorService {
//线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//值为29,用来表示偏移量
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池的最大容量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池的运行状态,总共有5个状态,用高3位来表示
private static final int RUNNING = -1 << COUNT_BITS; //接受新任务并处理阻塞队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS; //不接受新任务但会处理阻塞队列中的任务
private static final int STOP = 1 << COUNT_BITS; //不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务
private static final int TIDYING = 2 << COUNT_BITS; //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法
private static final int TERMINATED = 3 << COUNT_BITS; // terminated()方法已经执行结束
//任务缓存队列,用来存放等待执行的任务
private final BlockingQueue<Runnable> workQueue;
//全局锁,对线程池状态等属性修改时需要使用这个锁
private final ReentrantLock mainLock = new ReentrantLock();
//线程池中工作线程的集合,访问和修改需要持有全局锁
private final HashSet<Worker> workers = new HashSet<Worker>();
// 终止条件
private final Condition termination = mainLock.newCondition();
//线程池中曾经出现过的最大线程数
private int largestPoolSize;
//已完成任务的数量
private long completedTaskCount;
//线程工厂
private volatile ThreadFactory threadFactory;
//任务拒绝策略
private volatile RejectedExecutionHandler handler;
//线程存活时间
private volatile long keepAliveTime;
//是否允许核心线程超时
private volatile boolean allowCoreThreadTimeOut;
//核心池大小,若allowCoreThreadTimeOut被设置,核心线程全部空闲超时被回收的情况下会为0
private volatile int corePoolSize;
//最大池大小,不得超过CAPACITY
private volatile int maximumPoolSize;
//默认的任务拒绝策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//运行权限相关
private static final RuntimePermission shutdownPerm =
new RuntimePermission("modifyThread");
...
}
要約すると、上記のスレッド プールの設計から、スレッド プールの機能はまだ非常に完成されていることがわかります。
2.1.2. スレッドプールコンストラクター
//构造函数
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大允许线程数
long keepAliveTime, //线程存活时间
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //任务缓存队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) { //拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
要約する:
2.1.3. スレッドプールの実行
2.1.3.1. タスクの送信メソッド
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
submit メソッドの基になるメソッドも実行メソッドを呼び出していることがわかります。そのため、ここでは実行メソッドのみを分析します。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//第一步:创建核心线程
if (workerCountOf(c) < corePoolSize) { //worker数量小于corePoolSize
if (addWorker(command, true)) //创建worker
return;
c = ctl.get();
}
//第二步:加入缓存队列
if (isRunning(c) && workQueue.offer(command)) { //线程池处于RUNNING状态,将任务加入workQueue任务缓存队列
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //双重检查,若线程池状态关闭了,移除任务
reject(command);
else if (workerCountOf(recheck) == 0) //线程池状态正常,但是没有线程了,创建worker
addWorker(null, false);
}
//第三步:创建临时线程
else if (!addWorker(command, false))
reject(command);
}
要約すると、execute() メソッドの主な機能は次のとおりです。
2.1.3.2. スレッドの作成
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//等价于:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
//线程池已关闭,并且无需执行缓存队列中的任务,则不创建
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c)) //CAS增加线程数
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//上面的流程走完,就可以真实开始创建线程了
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); //这里创建了线程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); //这里将线程加入到线程池中
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); //添加成功,启动线程
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); //添加线程失败操作
}
return workerStarted;
}
概要: addWorker() メソッドの主な機能。
2.1.3.3. ワーカースレッドの実装
private final class Worker //Worker类是ThreadPoolExecutor的内部类
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread; //持有实际线程
Runnable firstTask; //worker所对应的第一个任务,可能为空
volatile long completedTasks; //记录执行任务数
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this); //当前线程调用ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
}
...继承AQS,实现了不可重入锁...
}
概要: ワーカー スレッド Worker クラスの主な機能。
2.1.3.4. スレッドの再利用
final void runWorker(Worker w) { //ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true; //标识线程是否异常终止
try {
while (task != null || (task = getTask()) != null) { //这里会不断从任务队列获取任务并执行
w.lock();
//线程是否需要中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task); //执行任务前的Hook方法,可自定义
Throwable thrown = null;
try {
task.run(); //执行实际的任务
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown); //执行任务后的Hook方法,可自定义
}
} finally {
task = null; //执行完成后,将当前线程中的任务制空,准备执行下一个任务
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); //线程执行完成后的清理工作
}
}
概要: runWorker() メソッドの主な機能。
2.1.3.5. 実行するタスクをキューから取得する
private Runnable getTask() {
boolean timedOut = false; //标识当前线程是否超时未能获取到task对象
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c)) //若线程存活时间超时,则CAS减去线程数量
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //允许超时回收则阻塞等待
workQueue.take(); //不允许则直接获取,没有就返回null
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
概要: getTask() メソッドの主な関数。
2.1.3.6. クリーンアップ作業
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w); //移除执行完成的线程
} finally {
mainLock.unlock();
}
tryTerminate(); //每次回收完一个线程后都尝试终止线程池
int c = ctl.get();
if (runStateLessThan(c, STOP)) { //到这里说明线程池没有终止
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false); //异常终止线程的话,需要在常见一个线程
}
}
概要: processWorkerExit() メソッドの主な機能。
2.1.3.7. スレッドプールの終了を試みる
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//若线程池正在执行、线程池已终止、线程池还需要执行缓存队列中的任务时,返回
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//执行到这里,线程池为SHUTDOWN且无待执行任务 或 STOP 状态
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE); //只中断一个线程
return;
}
//执行到这里,线程池已经没有可用线程了,可以终止了
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //CAS设置线程池终止
try {
terminated(); //执行钩子方法
} finally {
ctl.set(ctlOf(TERMINATED, 0)); //这里将线程池设为终态
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
概要: tryTerminate() メソッドの主な関数。
2.2. JAVAスレッドプールの概要
上記の JAVA スレッド プールの詳細な分析から、プロセスは複雑に見えますが、実際には状態の重複の検証、スレッドの安全性の保証など、多くの内容が含まれていることがわかります。機能は同じですが、いくつかの拡張機能が追加されています スレッドプールの機能を簡単に整理してみましょう。
2.2.1. 主な機能
2.2.2. 拡張機能
2.2.3. プロセスの概要
JAVA スレッド プール タスクの送信プロセスの上記の分析から、スレッド プール実行の単純なプロセスが次の図に示されていることがわかります。
2.3. JAVAスレッドプールの使用
上記のプロセスを検証するためのスレッド プールの基本的な使用法:
public static void main(String[] args) throws Exception {
//创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue(5));
//加入4个任务,小于核心线程,应该只有4个核心线程,队列为0
for (int i = 0; i < 4; i++) {
threadPoolExecutor.submit(new MyRunnable());
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 4
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
//再加4个任务,超过核心线程,但是没有超过核心线程 + 缓存队列容量,应该5个核心线程,队列为3
for (int i = 0; i < 4; i++) {
threadPoolExecutor.submit(new MyRunnable());
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 5
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 3
//再加4个任务,队列满了,应该5个热核心线程,队列5个,非核心线程2个
for (int i = 0; i < 4; i++) {
threadPoolExecutor.submit(new MyRunnable());
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 7
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
//再加4个任务,核心线程满了,应该5个热核心线程,队列5个,非核心线程5个,最后一个拒绝
for (int i = 0; i < 4; i++) {
try {
threadPoolExecutor.submit(new MyRunnable());
} catch (Exception e) {
e.printStackTrace(); //java.util.concurrent.RejectedExecutionException
}
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 10
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
System.out.println(threadPoolExecutor.getTaskCount()); //共执行15个任务
//执行完成,休眠15秒,非核心线程释放,应该5个核心线程,队列为0
Thread.sleep(1500);
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 5
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
//关闭线程池
threadPoolExecutor.shutdown();
}
200元の罰金と100万元以上を没収 You Yuxi: 高品質の中国語文書の重要性 MuskのJDK 21用ハードコア移行サーバー Solon、仮想スレッドは信じられないほど素晴らしい!!! TCP 輻輳制御によりインターネットが節約される OpenHarmony 用の Flutter が登場 Linux カーネルの LTS 期間が 6 年から 2 年に復元される Go 1.22 で for ループ変数エラーが修正される Svelte は「新しいホイール」を作成 - ルーン文字 Google が創立 25 周年を祝う著者: Qin Haoran、JD Retail
出典:JD Cloud Developer Community 転載の際は出典を明記してください