Java线程的学习_线程池
系统启动一个新线程需要很高的成本,因为它涉及与操作系统交互。在这种情况下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时。
线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池,线程池就会启动一个线程来run()或call()方法,当run()或call()方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run()或call()方法。除此之外,使用线程池可以有效地控制系统中并发线程的数量。
使用Executors类来创建线程池:
| 方法名 | |
|---|---|
| newCachedThreadPool() | 创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中 |
| newFixedThreadPool(int nThreads) | 创建一个可重用的、具有固定线程数的线程池 |
| newSingleThreadExecutor() | 创建一个只有单线程的线程池,它相当与调用newFixedThreadPool()方法时传入参数为1 |
| newScheduledThreadPool(int corePoolSize) | 创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池内。 |
| newSingleThreadScheduledExecutor() | 创建只有一个线程的线程池,它可以在指定延迟后执行线程任务 |
| ExecutorService newWorkStealingPool(int parallelism) | 创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争 |
| ExecutorService newWorkStealingPool() | 该方法是前一个方法的 简化版本,如果当前机器有4个CPU,则目标并行级别被设置为4,也就是相当于为前一个方法传入4作为参数。 |
使用线程池来执行线程任务的步骤:
1.调用Executors类的静态工厂方法创建一个ExecutorService对象,该对象代表一个线程池。
2.创建Runnable实现类或Callable实现类的实例,作为线程执行任务。
3.调用ExecutorService对象的submit()方法来提交Runnable实例或Callable实例。
4.当不想提交任何任务时,调用ExecutorService对象的shutdown()方法来关闭线程池。
使用线程池来执行指定Runnable对象所代表的的任务:
public class ThreadPoolTest {
public static void main(String[] args)throws Exception{
//创建一个具有固定线程数(6)的线程池
ExecutorService pool = Executors.newFixedThreadPool(6);
//使用Lambda表达式创建Runnable对象
Runnable target = () -> {
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + "的i值为:" + i);
}
};
//向线程池中提交两个线程
pool.submit(target);
pool.submit(target);
//关闭线程池
pool.shutdown();
}
}
ForkJoinPool类
ForkJoinPool类的作用是将一个任务拆分为多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。ForkJoinPool类是ExecutorService的实现类,因此是一种特殊的线程池。
| 方法 | |
|---|---|
| ForkJoinPool(int parallelism) | 创建一个包含parallelism个并行线程的ForkJoinPool |
| ForkJoinPool() | 以runtime.availableprocessors(处理器数量)返回值为parallelism参数来创建ForkJoinPool。 |
| ForkJoinPool commonPool() | 该方法返回一个通用池,通用池的运行状态不回受shutdown()或shutdownNow()方法的影响。 |
| int getCommonPoolParallelism() | 返回通用池的并行级别 |
创建了ForkJoinPool实例之后,就可调用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定任务。其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它还有两个抽象子类:RecursiveAction(没有返回值)和RecursiveTask(有返回值)。
没有返回值的“大任务”拆分:
//继承RecursiveAction来实现“可分解”的任务
class PrintTask extends RecursiveAction{
//每个“小任务”最多打印50个数
private static final int THRESHOLD = 50;
private int start;
private int end;
//打印从start到end的任务
public PrintTask(int start, int end){
this.start = start;
this.end = end;
}
@Override
protected void compute() {
// 当end与start之间的差小于THRESHOLD时,开始打印
if(end - start < THRESHOLD){
for(int i = 0; i < end; i++){
System.out.println(Thread.currentThread().getName() + "的i值:" + i);
}
}else{
//当end与start之间的差大于THRESHOLD时,开始打印
//将大任务分解成两个“小任务”
int middle = (start + end) / 2;
PrintTask left = new PrintTask(start, middle);
PrintTask right = new PrintTask(middle, end);
//并执行两个“小任务”
left.fork();
right.fork();
}
}
}
public class ForkJoinPoolTest {
public static void main(String[] args)throws Exception{
ForkJoinPool pool = new ForkJoinPool();
//提交可分解的PrintTask任务
pool.submit(new PrintTask(0, 300));
pool.awaitTermination(2, TimeUnit.SECONDS);
//关闭线程池
pool.shutdown();
}
}
有返回值的“大任务”:
//继承RecursiveTask来实现“可分解”的任务
class CalTask extends RecursiveTask<Integer>{
//每个“小任务”最多只累加20个数
private static final int THRESHOLD = 20;
private int arr[];
private int start;
private int end;
//累加从start到end数组
public CalTask(int[] arr, int start, int end){
this.arr = arr;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
// 当end与start之间的差小于THRESHOLD时,开始进行实际累加
if(end - start < THRESHOLD){
for(int i = start; i < end; i++){
sum += arr[i];
}
return sum;
}else{
//当end与start之间的差大于THRESHOLD,既要累加的数超过20个时,将大任务分解成两个小任务
int middle = (start + end) / 2;
CalTask left = new CalTask(arr, start, middle);
CalTask right = new CalTask(arr, middle, end);
//并执行两个小任务
left.fork();
right.fork();
//把两个小任务累加的结果合并起来
return left.join() + right.join();
}
}
}
public class Sum {
public static void main(String[] args)throws Exception{
int[] arr = new int[100];
Random rand = new Random();
int total = 0;
//初始化100个数字元素
for(int i = 0, len = arr.length; i < len; i++){
int tmp = rand.nextInt(20);
//对数组元素赋值,并将数组元素的值添加到sum总和中
System.out.println(tmp);
total += (arr[i] = tmp);
}
System.out.println("初始化统计出的和:" + total);
//创建一个通用池
ForkJoinPool pool = ForkJoinPool.commonPool();
//提交可分解的CaltTask任务
Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length));
System.out.println("使用CalTask计算出的和:" + future.get());
//关闭线程
pool.shutdown();
}
}Java线程的学习_线程池的更多相关文章
- java多线程面试题_线程并发面试题
1.什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个 ...
- Java并发编程学习:线程安全与锁优化
本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑 ...
- java并发编程学习: 守护线程(Daemon Thread)
在正式理解这个概念前,先把 守护线程 与 守护进程 这二个极其相似的说法区分开,守护进程通常是为了防止某些应用因各种意外原因退出,而在后台独立运行的系统服务或应用程序. 比如:我们开发了一个邮件发送程 ...
- java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService
前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...
- JAVA并发编程学习笔记------线程的三种创建方式
创建线程一般有如下几个方式: 1. 通过继承Thread类来创建一个线程: /** * 步骤1:定义一个继承Thread类的子类 * 步骤2:构造子类的一个对象 * 步骤3:启动线程: * */ pu ...
- Java学习笔记 线程池使用及详解
有点笨,参考了好几篇大佬们写的文章才整理出来的笔记.... 字面意思上解释,线程池就是装有线程的池,我们可以把要执行的多线程交给线程池来处理,和连接池的概念一样,通过维护一定数量的线程池来达到多个线程 ...
- Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析
Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ...
- java学习之线程池的实现
package com.gh.threadpoor; import java.util.concurrent.ExecutorService; import java.util.concurrent. ...
- java线程API学习 线程池ThreadPoolExecutor(转)
线程池ThreadPoolExecutor继承自ExecutorService.是jdk1.5加入的新特性,将提交执行的任务在内部线程池中的可用线程中执行. 构造函数 ThreadPoolExecut ...
随机推荐
- Luogu P4609 [FJOI2016]建筑师&&CF 960G Bandit Blues
考虑转化题意,我们发现其实就是找一个长度为\(n\)的全排列,使得这个排列有\(A\)个前缀最大值,\(B\)个后缀最大值,求方案数 我们考虑把最大值拎出来单独考虑,同时定义一些数的顺序排列为单调块( ...
- Kubernetes介绍与特性
1.Kubernetes 是什么 简单的来说,k8s可以理解为,一个容器平台,一个微服务平台,便携式云平台,我们那可以很快速的搭建一个服务,快速的运行起来 2.Kubernetes特性
- Easier Done Than Said?(应用到的知识)
memset(b,0,sizeof(b)) 对于大块儿内存的分配,例如int arr[100];定义了数组arr,包含100个元素,如果你写成int arr[100]=0;想将数组全部内容初始化为0, ...
- 制作新的train,test数据集
之前的数据集的train和test是直接按照网上下载的数据的前7000个作为训练集,后2212个作为测试集.看得出来,这个数据集是由开车录制视频转换来的图片数据,后面2000多个图片的场景和前面的场景 ...
- WINDOWS-API:API函数大全
操作系统除了协调应用程序的执行.内存分配.系统资源管理外,同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务是一个函数),可以帮肋应用程序达到开启视窗.描绘图形.使用周边设备的目的,由 ...
- HDU-2544-最短路(Bellman-Ford)
Bellman-Ford算法是一个时间复杂度很高,但是它可以用来判断负环 负环就是上面的图,那个环的整体值小于零了,所以就是负环. 我们用Bellman-Ford算法进行更新,打一个表出来: k a ...
- 【搜索 技巧】Letter gaps
需要一定技巧的搜索题 题目描述 记得做过这样一道题 Ponder This Challenge: In the string CABACB, each letter appears exactly t ...
- [CODEVS] 3955 最长严格上升子序列(加强版)
题目描述 Description 给一个数组a1, a2 ... an,找到最长的上升降子序列ab1<ab2< .. <abk,其中b1<b2<..bk. 输出长度即可. ...
- 永久激活IDEA的方法
第一步,下载破解补丁jetbrains-agent.jar 链接:https://pan.baidu.com/s/15x6dzOjveMkHlgHJT0PBWg提取码:2ykx 第二步,将下载的破解补 ...
- InnoDB体系架构总结(二)
事务 确保事务内的SQL都可以同步执行 要么一起成功 要么一起失败.事务有四个特性原子性 一致性,隔离性,持久性 实现方式 开始事务的时候回家记录记录一个LSN日志序列 当事务执行的时候 会首先在In ...