本文已参与「新人创作礼」活动,一起开启掘金创作之路。
线程池
在阿里巴巴的开发规约当中,有这么一条
为了防止创建销毁线程切换带来的开销,我们需要尽量使用线程池。
创建方法
在代码当中,有这么些个类
使用ThreadPoolExecutor创建线程,
ExecutorService service = new ThreadPoolExecutor(‘七个参数’);
我们debug进入ThreadPoolExecutor类当中,可以看到
参数解释
corePoolSize : 核心线程数,这部分线程就算没有执行也不会归还给操作系统
maximumPoolSize:最大线程数
keepAliveTime:存活时间,普通线程多长时间没有执行就会归还给操作系统
unit:keepAliveTime 单位
workQueue : 工作队列,装任务的BlockingQueue
threadFactory : 线程工厂,可以通过自定义线程工厂,来设置线程的名字,方便出问题排查
handler: 拒绝策略,如果队列满了,所有线程都忙着,这个时候再来新的任务,就执行拒绝策略
拒绝策略
jdk一共提供了四种拒绝策略:
abort 抛异常
discard 扔掉,不抛异常
discardOldest 扔掉排队时间最久的
callerRuns 调用者处理任务,谁调用谁来处理新来的任务
常见的线程池
在java当中可以使用Executors来创建常见的线程池
ExecutorService service = Executors.newFixedThreadPool(参数...)
newFixedThreadPool
如图,newFixedThreadPool 是一种 核心线程数和最大线程数一致 的线程池。BlockingQueue使用的是 LinkedBlockingQueue
LinkedBlockingQueue最大大小为Integer.MAX_VALUE
我们点开 ThreadPoolExecutor,可以看到工厂使用的是默认的线程工厂,拒绝策略使用的是 AbortPolicy,即队列满后来新的任务抛异常\
newCachedThreadPool
newCachedThreadPool 的特点:
核心线程数为0,
最大线程数为Integer.MAX_VALUE,
工作队列为 SynchronousQueue,SynchronousQueue是一种大小为0的队列。
在线程工厂和拒绝策略上面和newFixedThreadPool一样
即,newCachedThreadPool 的工作模式为:有线程来就执行,有空闲的线程由空闲的线程执行,没有空闲的线程则起新线程来执行
newSingleThreadExecutor
单线程的线程池,核心线程数和最大线程数都是1,其余的队列,工厂,执行策略可等和newFixedThreadPool一样。那么问题来了,为啥我不能单独创建一个线程而用线程池呢,个人感觉有几个有点
1、可以保证扔进去的任务的执行顺序
2、线程池可以帮助管理生命周期
newScheduledThreadPool
newScheduledThreadPool 的特殊之处在于,它的阻塞队列使用的是DelayedWorkQueue 是一个定时的阻塞队列,可以设定间隔多久执行。因此这个线程池是一个可以用于定时任务的线程池。
然而,这个线程池用的很少,面对复杂的定时任务,quartz是个更好的选择。
newWorkStealingPool
newWorkStealingPool 是一个ForkJoinPool。主要执行策略为 work stealing方法。每个线程维护自己的队列,自己队列任务执行完成了可以去拿别的线程未执行的任务。
ForkJoinPool 有两个执行任务 执行任务RecursiveTask:有返回值 RecursiveAction:无返回值
不带返回值的计算
import java.util.concurrent.RecursiveAction;
public class PrintTask extends RecursiveAction {
private static final int THRESHOLD = 9;
private int start;
private int end;
public PrintTask(int start, int end) {
super();
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if(end - start < THRESHOLD) {
for(int i=start;i<=end;i++) {
System.out.println(Thread.currentThread().getName()+",i="+i);
}
}else {
int middle = (start + end) / 2;
PrintTask firstTask = new PrintTask(start,middle);
PrintTask secondTask = new PrintTask(middle+1,end);
invokeAll(firstTask,secondTask);
}
}
}
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.TimeUnit;
public class ForkJoinPoolTest {
public static void main(String[] args) throws Exception{
testNoResultTask();
}
private static void testNoResultTask() throws InterruptedException{
ForkJoinPool pool = new ForkJoinPool();
pool.submit(new PrintTask(1,50));
pool.awaitTermination(2, TimeUnit.SECONDS);
pool.shutdown();
}
}
执行时能够将1-50并行的print出来。
带返回值的计算
import java.util.concurrent.RecursiveTask;
public class CalculateTask extends RecursiveTask<Integer> {
private static final int THRESHOLD = 49;
private int start;
private int end;
public CalculateTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= THRESHOLD) {
int result = 0;
for (int i = start; i <= end; i++) {
result += i;
}
return result;
} else {
int middle = (start + end) / 2;
CalculateTask firstTask = new CalculateTask(start, middle);
CalculateTask secondTask = new CalculateTask(middle + 1, end);
invokeAll(firstTask,secondTask);
return firstTask.join() + secondTask.join();
}
}
}
主函数如下
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.TimeUnit;
public class ForkJoinPoolTest {
public static void main(String[] args) throws Exception{
testHasResultTask();
}
public static void testHasResultTask() throws Exception {
int result1 = 0;
for (int i = 1; i <= 1000000; i++) {
result1 += i;
}
System.out.println("循环计算 1-1000000 累加值:" + result1);
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> task = pool.submit(new CalculateTask(1, 1000000));
int result2 = task.get();
System.out.println("并行计算 1-1000000 累加值:" + result2);
pool.awaitTermination(2, TimeUnit.SECONDS);
pool.shutdown();
}
}
具体的大家可以去网上搜索看看。