线程池

由于启动一个线程要与操作系统交互,所以系统启动一个新的线程的成本是比较高的。在这种情况下,使用线程池可以很好的提升性能,特别是程序中涉及创建大量生命周期很短暂的线程时。

与数据库连接池类似,线程池在启动时就创建了大量的空闲的线程,程序将一个Runnable对象或者Callable对象传给线程池,线程池就会启动一个线程来执行他们的run()或call()方法,当方法执行结束后,线程并不会死亡,而是再次返回线程池中成为空闲状态,等待下一次执行。

线程池还可以有效的控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能剧烈下降,甚至导致JVM崩溃,而线程池中的最大线程参数可以控制系统中并发线程数不超过此数。

一、Executors工厂生成线程池

在java5开始,增加了一个Executors工厂类来生产线程池,它包含如下几个静态方法来生产线程池:

newCacheThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会别缓存在线程池中。

newFixedThreadPool(int nThread):创建一个可重用的、具有固定线程数的线程池。

newSingleThreadExecutor():创建一个只有单线程的线程池,相当与newFixedThreadPool(1)。

newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在延迟后执行线程任务。

newSigleScheduleExecutor():创建只有一个线程的线程池,它可以在指定延迟后执行线程任务。

上面方法中,前3个返回ExecutorService对象线程池。后面两个返回ScheduleExecutorService对象线程池。ScheduleExecutorService是ExecutorService的子类,可以延迟执行线程。

线程池小例子:

package threadtest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ThreadTest implements Runnable { private static int i = 0;
private synchronized void incre() {
i++;
System.out.println(Thread.currentThread().getName() +" "+ i);
}
@Override
public void run() { for(int j=0;j<5;j++) {
incre();
}
}
public static void main(String[] args) { //生产一个线程池
ExecutorService es = Executors.newFixedThreadPool(6);
ThreadTest tt = new ThreadTest();
es.submit(tt);
es.submit(tt);
es.submit(tt);
es.shutdown();
}
}

结果:

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-2 6
pool-1-thread-2 7
pool-1-thread-2 8
pool-1-thread-2 9
pool-1-thread-2 10
pool-1-thread-3 11
pool-1-thread-3 12
pool-1-thread-3 13
pool-1-thread-3 14
pool-1-thread-3 15

二、ForkJoinPool

java7提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的计算结果合并成总的计算结果。(跟进多核cpu时代)

ForkJoinPool是ExecutorService接口的实现类,所以它是一种特殊的线程池

ForkJoinPool有两个常用的构造器:

ForkJoinPool(int parallelism):创建包含parallelism个并行线程的ForkJoinPool

ForkJoinPool():以Runtime.availableProcessors()方法的返回值做为parallelism参数来创建ForkJoinPool。

ForkJoinPool创建后就可以调用它的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定任务了。其中ForkJoinTask代表一个可以并行、合并的任务。

ForkJoinTask是一个抽象类,它还有两个抽象子类:RecursiveAction和RecursiveTask。其中RecursiveTask代表有返回值的任务,RecursiveAction代表无返回值的任务。

下面程序利用ForkJoinPool执行RecursiveAction任务,打印一段数字

package threadtest;

import java.util.concurrent.RecursiveAction;

/**
* 继承RecursiveAction实现可分解的任务
* @author rdb
*
*/
public class PrintTask extends RecursiveAction{ //每个任务对多打印50个数
private static final int THRESHOLD = 50;
private int start;
private int end;
public PrintTask(int start,int end) {
this.start = start;
this.end = end ;
}
@Override
protected void compute() {
//打印数小于50开始打印,否则分解任务
if(end - start < THRESHOLD) {
for(int i = start;i<end;i++) {
System.out.println(Thread.currentThread().getName()+" "+ i);
}
}else {
int mind = (end + start)/2;
PrintTask left = new PrintTask(start, mind);
PrintTask right = new PrintTask(mind, end);
//分解任务
left.fork();
right.fork(); }
}
} package threadtest; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit; public class ThreadTest { public static void main(String[] args) throws InterruptedException { //构建ForkJoinPool
ForkJoinPool pool = new ForkJoinPool();
//提交任务
pool.submit(new PrintTask(0, 300));
pool.shutdown(); //阻塞主线程,直到线程池关闭
while(!pool.awaitTermination(2, TimeUnit.SECONDS)) {
System.out.println("service not stop");
}
System.out.println("all thread complete");
} }

结果:从结果中可以看出,有个四个核在并行执行任务(电脑是4核处理器)

ForkJoinPool-1-worker-1 262
ForkJoinPool-1-worker-0 37
ForkJoinPool-1-worker-3 187
ForkJoinPool-1-worker-3 188
ForkJoinPool-1-worker-3 189
ForkJoinPool-1-worker-3 190
ForkJoinPool-1-worker-3 191
ForkJoinPool-1-worker-2 112
ForkJoinPool-1-worker-2 113
ForkJoinPool-1-worker-3 192
...
ForkJoinPool-1-worker-0 259
ForkJoinPool-1-worker-0 260
ForkJoinPool-1-worker-1 296
ForkJoinPool-1-worker-0 261
ForkJoinPool-1-worker-1 297
ForkJoinPool-1-worker-1 298
ForkJoinPool-1-worker-1 299
all thread complete

下面程序利用ForkJoinPool执行RecursiveTask任务,求数组的和并返回

package threadtest;

import java.util.concurrent.RecursiveTask;
/**
* 继承RecursiveTask实现可分解的有返回值的任务
* @author rdb
*
*/
public class SumTask extends RecursiveTask<Integer>{ //定义每个任务最多求和10个元素
private final int THRESHOLD = 10;
private int[] array;
private int start;
private int end;
public SumTask(int[] array,int start,int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
if((end - start) < THRESHOLD) {
for(int i = start;i<end;i++) {
sum += array[i];
}
return sum;
}else {
int mind = (end + start)/2 ;
SumTask left = new SumTask(array, start, mind);
SumTask right = new SumTask(array, mind, end);
left.fork();
right.fork();
return left.join()+right.join();
}
} } package threadtest; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future; public class ThreadTest { public static void main(String[] args) throws Exception {
int[] array = new int[100] ;
int total = 0; for(int i = 0;i<array.length;i++) {
array[i] = (int) (Math.random() * 10);
total += array[i];
}
System.out.println(total);
//JAVA8新加的通用池
ForkJoinPool pool = ForkJoinPool.commonPool();
Future<Integer> future = pool.submit(new SumTask(array, 0, array.length));
System.out.println(future.get());
}
}

结果

437
437

java并发编程基础——线程池的更多相关文章

  1. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  2. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  3. (转)Java并发编程:线程池的使用

    背景:线程池在面试时候经常遇到,反复出现的问题就是理解不深入,不能做到游刃有余.所以这篇博客是要深入总结线程池的使用. ThreadPoolExecutor的继承关系 线程池的原理 1.线程池状态(4 ...

  4. Java并发编程:线程池的使用(转载)

    转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  5. Java并发编程:线程池的使用(转载)

    文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  6. [转]Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  7. 【转】Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  8. 13、Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  9. Java并发编程基础-线程安全问题及JMM(volatile)

    什么情况下应该使用多线程 : 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占 ...

随机推荐

  1. httprunner 2.5.7 下.env 文件环境变量的使用及debugtalk的使用,对test的参数化及执行

    一.httprunner 2.5.7 下.env  文件的使用 1..env 文件配置如下: 2.debugtalk.py 编写如下: 在debugtalk.py中增加开始和结束执行语句: 3.需要做 ...

  2. 最小高度树Java版本(力扣)

    最小高度树 给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树. 示例:给定有序数组: [-10,-3,0,5,9],一个可能的答案是:[0,-3,9,-10, ...

  3. 对标 Spring Boot & Cloud ,轻量框架 Solon 1.4.14 发布

    Solon 是一个轻量的Java基础开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Job.Micro service.WebS ...

  4. 【NX二次开发】三点画圆,三角形外心,已知三点求圆心

    已知P1.P2.P3,求点O 算法:三点不在一条直线上时,通过连接任意两点,作中垂线.任意两条中垂线的交点是圆心.

  5. 【NX二次开发】批量数字签名的方法,解决自己电脑编译的dll在用户正版NX无法使用的问题

    在UG5.0开始,所有开发的DLL都要"签名"后才能被客户端上正版的NX调用. 1. 如果是基于c++开发的dll,使用如下方法可以顺利签名成功(这里借用网上现有的文字和图片) 1 ...

  6. 【NX二次开发】设置了“附加包含目录”,还是提示“无法打开包括文件”的解决方法

    项目属性中的"附加包含目录"路径完全正确,但是还是无法找到头文件: 这个问题我遇到过不止一次,纠结了很久,终于发现了解决方法: 改为: 问题解决! 分析原因:项目中的属性配置 与 ...

  7. 01 . Varnish简介,原理,配置缓存

    简介 Varnish是高性能开源的反向代理服务器和HTTP缓存服务器,其功能与Squid服务器相似,都可以用来做HTTP缓存.可以安装 varnish 在任何web前端,同时配置它缓存内容.与传统的 ...

  8. [Azure DevOps] 如何使用任务组

    1. 使用 PowerShell 脚本 在上一篇文章中我们学会了怎么使用扩展在编译前实时更改版本号.有些情况下我们希望不适用扩展,例如喜欢发明轮子,或者根本没有安装扩展的权限.这时候我们可以自己写 P ...

  9. NXNSAttack漏洞简析

    漏洞简介: 该漏洞为DNS 放大攻击,是 DDoS 攻击,攻击者利用 DNS 服务器中的漏洞将小查询转换为可能破坏目标服务器的更大负载. 在 NXNSAttack 的情况下,远程攻击者可以通过向易受攻 ...

  10. kubernetes的网络代理模式

    在k8s中,如果想ping svc以及ip,发现无法ping通,使用测试环境为k8s 1.6,后来k8s升级到1.12版本,发现ping svc以及ip可以ping通,这里分析一下原因. 后来发现是由 ...