背景:Java5开始,Java内建支持线程池。
一:
通过Executors工厂类来创建线程池包含如下方法
-
newCachedThreadPool( ):创建一个具有缓存功能的线程池;
-
newFixedThreadExecutor( ):创建一个只有单线程的线程池;
-
newSingleThreadExecutor( ):创建一个只有单线程的线程池
-
newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池
-
newSingleThreadScheduledExecutor( ):创建只有一个线程的线程池。
-
ExecutorService newWorkStealingPool(int parallelism):创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争。
-
ExecutorService newWorkStealingPool( ):该方法是前一个方法的简化版本。如果机器有4个CPU,则目标并行级别被设置为4。
使用线程池来执行线程任务的步骤如下
-
调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程
-
创建Runnable实现类或Callable实现类的实例,作为线程执行任务
-
调用ExecutorService对象的submit()方法来提交Runnable实例或Callable实例
-
当不想提交任何任务时,调用ExecutorService对象的shutdown( )方法来关闭线程池。
具体执行方式:
//创建一个具有固定线程数的线程池
ExecutorService pool = Executors.newFixedThreadPool(6);
//使用Lambda表达式创建Runnable对象
Runnable target =()->{
for(var i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+"的i值为"+i);
}
};
//向线程池中提交两个线程
pool.submit(target);
pool.submit(target);
//关闭线程池
pool.shutdown();
二:
使用ForkJoinPool利用多CPU(将一个任务拆分成多个小任务放到多个处理器核心上并行执行,再将所有任务的执行结果合并)
ForkJoinPool是ExecutorService的实现类。是一种特殊的线程池
ForkJoinPool提供如下两个构造器:
-
ForkJoinPool(int a):创建一个包含a个并行线程的ForkJoinPool
-
ForkJoinPool():以Runtime.availableProcessors( )方法的返回值作为a参来来创建ForkJoinPool
ForkJoinPool类通过如下两个方法提供通用池功能
-
ForkJoinPool commonPool( ):该方法返回一个通用池,通用池的运行状态不会受shutdown( )或shutdownNow( )方法的影响。可以直接执行System.exit(0)终止正在执行的任务
-
int getCommonPoolParallelism( ):该方法返回通用池的并行级别。
创建ForkJoinPool实例之后,调用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定任务。ForkJoinTask代表一个可以并行,合并的任务
注:ForkJoinTask是一个抽象类,它有两个抽象子类:RecursiveAction和RecursiveTask。前者代表没有返回值的任务,后者代表有返回值的任务。
// 将大任务分解成两个小任务。
int middle = (start + end) / 2;
var left = new PrintTask(start, middle);
var right = new PrintTask(middle, end);
// 并行执行两个“小任务”
left.fork();
right.fork();
var pool = new ForkJoinPool();
// 提交可分解的PrintTask任务
pool.submit(new PrintTask(0, 300));
pool.awaitTermination(2, TimeUnit.SECONDS);
// 关闭线程池
pool.shutdown();
遗忘点:
awaitTermination(2, TimeUnit.SECONDS);
2指的是等待关闭线程执行器的时间大小,第一个时间参数的单位
注: fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。