[高い同時実行性]ソースコードの観点からスレッドプールを作成する方法は何ですか?

みなさん、こんにちは。氷河です~~

Javaの同時実行性の高い分野では、スレッドプールは常に避けられないトピックでした。一部の子供靴はスレッドプールを使用していますが、スレッドプールを作成する方法は、Executorsツールクラスの使用の邪魔になるだけです。スレッドプールを作成する方法は何ですか?スレッドプールを作成するソースコードからスレッドプールを作成する方法を詳しく見てみましょう。

Executorsユーティリティクラスを使用してスレッドプールを作成します

スレッドプールを作成する場合、初心者はツールクラスExecutorsを最もよく使用し、このツールクラスを使用してスレッドプールを作成するのは非常に簡単です。スレッドプールの詳細にあまり注意を払う必要はなく、必要なのは必要なパラメータを渡すために。Executorsユーティリティクラスは、以下に示すように、スレッドプールを作成するためのいくつかのメソッドを提供します。

  • Executors.newCachedThreadPool:キャッシュ可能なスレッドプールを作成します。スレッドプールのサイズが必要以上の場合、アイドル状態のスレッドを柔軟にリサイクルできます。リサイクル可能なスレッドがない場合は、新しいスレッドを作成します。
  • Executors.newFixedThreadPool:固定長のスレッドプールを作成します。これにより、同時スレッドの最大数を制御でき、余分なスレッドはキューで待機します
  • Executors.newScheduledThreadPool:スケジュールされた定期的なタスク実行をサポートするために、固定長のスレッドプールを作成します
  • Executors.newSingleThreadExecutor:シングルスレッドスレッドプールを作成し、一意のワーカースレッドを使用してタスクを実行し、すべてのタスクが指定された順序(先入れ先出し、優先順位)で実行されるようにします。
  • Executors.newSingleThreadScheduledExecutor:スケジュールされた定期的なタスク実行をサポートするシングルスレッドスレッドプールを作成します
  • Executors.newWorkStealingPool:並列処理レベルでワークスティーリングスレッドプールを作成します

その中でも、Executors.newWorkStealingPoolメソッドは、Java 8でスレッドプールを作成するための新しいメソッドです。スレッドプールの並列処理レベルを設定でき、同時実行性とパフォーマンスが向上します。このメソッドに加えて、スレッドプールを作成する他のメソッドは、基本的にThreadPoolExecutorクラスのコンストラクターを呼び出します。

たとえば、次のコードを使用してスレッドプールを作成できます。

Executors.newWorkStealingPool();
Executors.newCachedThreadPool();
Executors.newScheduledThreadPool(3);
复制代码

ThreadPoolExecutorクラスを使用してスレッドプールを作成します

コード構造に関しては、ThreadPoolExecutorクラスはAbstractExecutorServiceを継承します。つまり、ThreadPoolExecutorクラスはAbstractExecutorServiceクラスのすべての機能を備えています。

Executorsツールクラスで作成されたスレッドプールのほとんどはThreadPoolExecutorクラスのコンストラクターを呼び出すため、Executorsツールクラスを使用する代わりに、ThreadPoolExecutorクラスのコンストラクターを直接呼び出してスレッドプールを作成することもできます。次に、ThreadPoolExecutorクラスのコンストラクターを見てみましょう。

ThreadPoolExecutorクラスのすべてのコンストラクターを以下に示します。

public ThreadPoolExecutor(int corePoolSize,
			      int maximumPoolSize,
			      long keepAliveTime,
			      TimeUnit unit,
			     BlockingQueue<Runnable> workQueue) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
		 Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
				int maximumPoolSize,
				long keepAliveTime,
				TimeUnit unit,
				BlockingQueue<Runnable> workQueue,
			        ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
	 threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
				int maximumPoolSize,
				long keepAliveTime,
			        TimeUnit unit,
				BlockingQueue<Runnable> workQueue,
				RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
	 Executors.defaultThreadFactory(), handler);
}

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.acc = System.getSecurityManager() == null ?
			null :
			AccessController.getContext();
	this.corePoolSize = corePoolSize;
	this.maximumPoolSize = maximumPoolSize;
	this.workQueue = workQueue;
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	this.threadFactory = threadFactory;
	this.handler = handler;
}
复制代码

ThreadPoolExecutorクラスのコンストラクターのソースコードからわかるように、スレッドプールを作成するために最終的に呼び出されるコンストラクターは次のとおりです。

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.acc = System.getSecurityManager() == null ?
			null :
			AccessController.getContext();
	this.corePoolSize = corePoolSize;
	this.maximumPoolSize = maximumPoolSize;
	this.workQueue = workQueue;
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	this.threadFactory = threadFactory;
	this.handler = handler;
}
复制代码

この構築メソッドの各パラメーターの意味と機能については、「高並行性-私が言わなければならないスレッドプールとThreadPoolExecutorクラスの分析」を参照してください。

ThreadPoolExecutorクラスのコンストラクターを自分で呼び出すことにより、スレッドプールを作成できます。たとえば、次の形式を使用してスレッドプールを作成できます。

new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                       60L, TimeUnit.SECONDS,
                       new SynchronousQueue<Runnable>());
复制代码

ForkJoinPoolクラスを使用してスレッドプールを作成します

Java8のExecutorsツールクラスには、スレッドプールを作成するための次のメソッドが追加されています。

public static ExecutorService newWorkStealingPool(int parallelism) {
	return new ForkJoinPool
		(parallelism,
		 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
		 null, true);
}

public static ExecutorService newWorkStealingPool() {
	return new ForkJoinPool
		(Runtime.getRuntime().availableProcessors(),
		 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
		 null, true);
}
复制代码

ソースコードから、はい、本質的に、ForkJoinPoolクラスのコンストラクタークラスが呼び出されてスレッドプールが作成され、コード構造の観点から、ForkJoinPoolクラスはAbstractExecutorService抽象クラスを継承します。次に、ForkJoinPoolクラスのコンストラクターを見てみましょう。

public ForkJoinPool() {
	this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
		 defaultForkJoinWorkerThreadFactory, null, false);
}
 public ForkJoinPool(int parallelism) {
	this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
}

public ForkJoinPool(int parallelism,
				ForkJoinWorkerThreadFactory factory,
				UncaughtExceptionHandler handler,
				boolean asyncMode) {
	this(checkParallelism(parallelism),
		 checkFactory(factory),
		 handler,
		 asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
		 "ForkJoinPool-" + nextPoolId() + "-worker-");
	checkPermission();
}

private ForkJoinPool(int parallelism,
				 ForkJoinWorkerThreadFactory factory,
				 UncaughtExceptionHandler handler,
				 int mode,
				 String workerNamePrefix) {
	this.workerNamePrefix = workerNamePrefix;
	this.factory = factory;
	this.ueh = handler;
	this.config = (parallelism & SMASK) | mode;
	long np = (long)(-parallelism); // offset ctl counts
	this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
复制代码

ソースコードを見ると、ForkJoinPoolのコンストラクターが最終的に次のプライベートコンストラクターを呼び出すことがわかります。

private ForkJoinPool(int parallelism,
				 ForkJoinWorkerThreadFactory factory,
				 UncaughtExceptionHandler handler,
				 int mode,
				 String workerNamePrefix) {
	this.workerNamePrefix = workerNamePrefix;
	this.factory = factory;
	this.ueh = handler;
	this.config = (parallelism & SMASK) | mode;
	long np = (long)(-parallelism); // offset ctl counts
	this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
复制代码

各パラメータの意味は次のとおりです。

  • 並列処理:並行性レベル。
  • factory:スレッドを作成するファクトリクラスオブジェクト。
  • handler:当线程池中的线程抛出未捕获的异常时,统一使用UncaughtExceptionHandler对象处理。
  • mode:取值为FIFO_QUEUE或者LIFO_QUEUE。
  • workerNamePrefix:执行任务的线程名称的前缀。

当然,私有构造方法虽然是参数最多的一个方法,但是其不会直接对外方法,我们可以使用如下方式创建线程池。

new ForkJoinPool();
new ForkJoinPool(Runtime.getRuntime().availableProcessors());
new ForkJoinPool(Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
复制代码

使用ScheduledThreadPoolExecutor类创建线程池

在Executors工具类中存在如下方法类创建线程池。

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
	return new DelegatedScheduledExecutorService
		(new ScheduledThreadPoolExecutor(1));
}

public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
	return new DelegatedScheduledExecutorService
		(new ScheduledThreadPoolExecutor(1, threadFactory));
}

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
	return new ScheduledThreadPoolExecutor(corePoolSize);
}

public static ScheduledExecutorService newScheduledThreadPool(
		int corePoolSize, ThreadFactory threadFactory) {
	return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
复制代码

从源码来看,这几个方法本质上调用的都是ScheduledThreadPoolExecutor类的构造方法,ScheduledThreadPoolExecutor中存在的构造方法如下所示。

public ScheduledThreadPoolExecutor(int corePoolSize) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue());
}

public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), threadFactory);
}

public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), handler);
}

public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory, RejectedExecutionHandler handler) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), threadFactory, handler);
}
复制代码

而从代码结构上看,ScheduledThreadPoolExecutor类继承自ThreadPoolExecutor类,本质上还是调用ThreadPoolExecutor类的构造方法,只不过此时传递的队列为DelayedWorkQueue。我们可以直接调用ScheduledThreadPoolExecutor类的构造方法来创建线程池,例如以如下形式创建线程池。

new ScheduledThreadPoolExecutor(3)
复制代码

好了,今天就到这儿吧,我是冰河,我们下期见~~

おすすめ

転載: juejin.im/post/7101855019565056031