线程池
- newFixedThreadPool
- newCachedThreadPool
- newScheduledThreadPool
- newWorkStealingPool
- ForkJoinPool
- 使用说明
Jdk中Executors为我们提供了七种常用线程池工具类,分别是:
- newFixedThreadPool:创建一个固定数目的、可重用的线程池。
- newCachedThreadPool:创建一个可缓存线程池,可灵活回收线程。
- newScheduledThreadPool:创建一个定时线程池,支持周期性的任务执行。
- newSingleThreadExecutor:创建一个单线程化的线程池,唯一的工作线程来执行任务,任务按照制定顺序(FIFO、LIFO、优先级)来执行。
- newSingleThreadScheduledExecutor::创建一个单线程化定时线程池,定期或延时执行任务。
- newWorkStealingPool:并行级别的工作窃取模式的线程池,默认是大小CPU的核数。(1.8)
- ForkJoinPool:支持大任务分解成小任务的线程池,通常配合ForkJoinTask接口的子类RecursiveAction或RecursiveTask使用。(1.8)
newFixedThreadPool
根据提交的任务是否创建线程,如果核心线程未满,则创建线程执行,不满则放置队列中。
public class Test {
public static void main(String[] args) {
ExecutorService executorService= Executors.newFixedThreadPool(5);
for (int i = 1; i <11 ; i++) {
int count = i;
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了任务"+ count);
});
}
executorService.shutdown();
}
}
运行结果:
pool-1-thread-1执行了任务1
pool-1-thread-5执行了任务5
pool-1-thread-4执行了任务4
pool-1-thread-3执行了任务3
pool-1-thread-2执行了任务2
pool-1-thread-1执行了任务6
pool-1-thread-4执行了任务8
pool-1-thread-2执行了任务9
pool-1-thread-5执行了任务7
pool-1-thread-3执行了任务10
newCachedThreadPool
提交的任务如果没有空闲线程则新建,如果有复用空闲线程,空间线程默认60秒被回收。
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 1; i <11 ; i++) {
int count = i;
//Thread.sleep(100*count);
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了任务"+ count);
});
}
executorService.shutdown();
}
}
运行结果:
pool-1-thread-1执行了任务1
pool-1-thread-4执行了任务4
pool-1-thread-2执行了任务2
pool-1-thread-3执行了任务3
pool-1-thread-5执行了任务5
pool-1-thread-6执行了任务6
pool-1-thread-7执行了任务7
pool-1-thread-8执行了任务8
pool-1-thread-9执行了任务9
pool-1-thread-10执行了任务10
解开Thread.sleep(100*count)执行结果如下:
pool-1-thread-1执行了任务1
pool-1-thread-1执行了任务2
pool-1-thread-1执行了任务3
pool-1-thread-1执行了任务4
pool-1-thread-1执行了任务5
pool-1-thread-1执行了任务6
pool-1-thread-1执行了任务7
pool-1-thread-1执行了任务8
pool-1-thread-1执行了任务9
pool-1-thread-1执行了任务10
newScheduledThreadPool
周期性的执行任务,有三种方式来执行。
- schedule为多长时间后执行任务,仅执行一次。
- scheduleAtFixedRate多长时间后执行任务,然后按照周期不停的去执行。
- scheduleWithFixedDelay跟scheduleAtFixedRate类似,不同在于:前者是任务执行完后再隔多长时间执行,而后者不管执行是否结束,到达时间点就执行。
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":newScheduledThreadPool 开始....");
executorService.schedule(()->{
System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":schedule 3 秒后运行");
},3, TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(()->{
try {
System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":scheduleAtFixedRate 5 秒后运行 2秒运行一次");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},5,2,TimeUnit.SECONDS);
executorService.scheduleWithFixedDelay(()->{
try {
System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now())+":scheduleAtFixedRate 10 秒后运行 2秒运行一次");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},5,2,TimeUnit.SECONDS);
运行结果:
2019-06-26 02:26:57:newScheduledThreadPool 开始…
2019-06-26 02:27:00:schedule 3 秒后运行
2019-06-26 02:27:02:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:02:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:07:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:09:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:12:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:16:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:17:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:22:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:23:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:27:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:30:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:32:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:37:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:37:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:42:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:44:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:47:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:51:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:27:52:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:57:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:27:58:scheduleAtFixedRate 10 秒后运行 2秒运行一次
2019-06-26 02:28:02:scheduleAtFixedRate 5 秒后运行 2秒运行一次
2019-06-26 02:28:05:scheduleAtFixedRate 10 秒后运行 2秒运行一次
newWorkStealingPool
会创建一个含有足够多线程的线程池,来维持相应的并行级别,它是一种工作窃取模式,每个处理器的核都对应着处理的任务,当一个核的任务处理完后,会帮助其他核来处理任务。
// 默认为系统的核数
ExecutorService executorService = Executors.newWorkStealingPool(4);
for (int i = 0; i < 10; i++) {
int count = i;
executorService.execute(()->{
try {
Thread.sleep(count *100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
// WorkStealingPool是一种后台线程,要不然看不到运行结果。
System.in.read();
运行结果:
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-0
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-0
ForkJoinPool-1-worker-2
通过结果可以看出,当线程1执行完毕后,它会在帮忙再次执行其他任务。
ForkJoinPool
在并发编程中,我们经常遇见把大任务拆分成小任务来执行,ForkJoinPool是一种支持任务分解的线程池,按照预先设置好的规则来拆分任务,最后合并。一般配合分配任务接口ForkJoinTask来使用,ForkJoinTask有两个实现类:RecursiveAction和RecursiveTask。区别在于前者没有返回值,后者有返回值。
// 最大任务量
private static final int MAX_NUMBER = 1000000;
private static int[] nums = new int[5000000];
static {
for (int i = 0; i < nums.length; i++) {
nums[i] = new Random().nextInt(100)+1;
}
}
public static void main(String[] args) throws InterruptedException,ExecutionException {
System.out.println("结果为:"+ Arrays.stream(nums).sum());
// 默认为系统的核数
ForkJoinPool forkJoinPool = new ForkJoinPool();
MyTask myTask = new MyTask(0, nums.length);
forkJoinPool.submit(myTask);
System.out.println("结果为:"+myTask.get());
}
static class MyTask extends RecursiveTask<Long>{
private int start;
private int end;
public MyTask(int start, int end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start < MAX_NUMBER){
// 不用再分任务
long result = 0;
for (int i = start; i < end; i++) {
result = result + nums[i];
}
System.out.println("from:"+start+"---to:"+end+"----="+result);
return result;
}else {
// 继续切分任务
int middle = start + (end - start)/2 ;
MyTask leftTask = new MyTask(start, middle);
MyTask rightTask = new MyTask(middle, end);
// 执行任务
leftTask.fork();
rightTask.fork();
return leftTask.join()+rightTask.join();
}
}
}
运行结果:
结果为:252400222
from:2500000—to:3125000----=31505184
from:1875000—to:2500000----=31552670
from:3125000—to:3750000----=31623470
from:0—to:625000----=31505421
from:3750000—to:4375000----=31531536
from:1250000—to:1875000----=31554030
from:625000—to:1250000----=31544988
from:4375000—to:5000000----=31582923
结果为:252400222
注意:ForkJoinPool在执行过程中,会创建大量的子任务,导致GC进行垃圾回收。
使用说明
阿里的解释足够权威了吧,自己做一个线程池。
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), new MyThreadFactory(), (r, executor1) -> System.out.println( executor1.toString()));
for (int i = 0; i < 10; i++) {
executor.execute(()->{
try {
Thread.sleep((new Random().nextInt(100)+1)*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"执行了");
System.out.println( executor.toString());
});
}
}
static class MyThreadFactory implements ThreadFactory {
private final AtomicInteger mThreadNumber = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"my-thread-" + mThreadNumber.incrementAndGet());
}
}
运行结果:
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 5, completed tasks = 0]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 4, completed tasks = 1]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 3, completed tasks = 2]
Thread[my-thread-2,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 3]
Thread[my-thread-1,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 1, completed tasks = 4]
Thread[my-thread-2,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 5]
Thread[my-thread-3,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 6]
Thread[my-thread-1,5,main]执行了
java.util.concurrent.ThreadPoolExecutor@50040f0c[Running, pool size = 3, active threads = 1, queued tasks = 0, completed tasks = 7]
至于参数和结果问题可以查看上一章。