系统启动一个新线程需要很高的成本,因为它涉及与操作系统交互。在这种情况下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时。

线程池在系统启动时即创建大量空闲的线程,程序将一个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线程的学习_线程池的更多相关文章

  1. java多线程面试题_线程并发面试题

    1.什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个 ...

  2. Java并发编程学习:线程安全与锁优化

    本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑 ...

  3. java并发编程学习: 守护线程(Daemon Thread)

    在正式理解这个概念前,先把 守护线程 与 守护进程 这二个极其相似的说法区分开,守护进程通常是为了防止某些应用因各种意外原因退出,而在后台独立运行的系统服务或应用程序. 比如:我们开发了一个邮件发送程 ...

  4. java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

  5. JAVA并发编程学习笔记------线程的三种创建方式

    创建线程一般有如下几个方式: 1. 通过继承Thread类来创建一个线程: /** * 步骤1:定义一个继承Thread类的子类 * 步骤2:构造子类的一个对象 * 步骤3:启动线程: * */ pu ...

  6. Java学习笔记 线程池使用及详解

    有点笨,参考了好几篇大佬们写的文章才整理出来的笔记.... 字面意思上解释,线程池就是装有线程的池,我们可以把要执行的多线程交给线程池来处理,和连接池的概念一样,通过维护一定数量的线程池来达到多个线程 ...

  7. Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析

    Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ...

  8. java学习之线程池的实现

    package com.gh.threadpoor; import java.util.concurrent.ExecutorService; import java.util.concurrent. ...

  9. java线程API学习 线程池ThreadPoolExecutor(转)

    线程池ThreadPoolExecutor继承自ExecutorService.是jdk1.5加入的新特性,将提交执行的任务在内部线程池中的可用线程中执行. 构造函数 ThreadPoolExecut ...

随机推荐

  1. make 与makefile(会不会写 makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。)

    跟我一起写 Makefile /**/ 陈皓 (CSDN) 概述 —— 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉 ...

  2. Thread and Peocess

    Thread and Peocess pthread_create() 原型: int pthread_create(pthread_t* thread, pthread_attr_t* attr, ...

  3. @ConditionalOnProperty来控制Configuration是否生效

    1. 简介 Spring Boot通过@ConditionalOnProperty来控制Configuration是否生效 2. 说明 @Retention(RetentionPolicy.RUNTI ...

  4. DS博客作业08--课程总结

    DS博客作业08--课程总结 1.当初你是如何做出选择计算机专业的决定的? 1.1 经过一年学习,你的看法改变了么,为什么? 1.2 你觉得计算机是你喜欢的领域吗,它是你擅长的领域吗? 为什么? 1. ...

  5. input提示文字;placeholder字体修改

    在很多网站上我们都看到input输入框显示提示文字,让我们一起来看看如果在input输入框中显示提示文字.我们只需要在<input>标签里添加:placeholder="提示文字 ...

  6. Android之父Andy Rubin:被乔布斯羡慕嫉妒的天才

    今年中国掀起一股“苹果热”,智能手机iPhone.平板电脑iPad遭疯抢,一度卖断货.然而,令许多人意想不到的是,在“苹果”的老家——美国市场,智能手机中卖得最火的并不是iPhone,而是Androi ...

  7. 【Qt】2.2 继续了解信号和槽

    槽和普通成员函数一样,可以是虚函数.被重载,可以是公有.私有.保护的.它可以被其它C++成员函数调用. 槽连接了信号,当发射这个信号时,槽会被自动调用. 连接函数: bool QObject::con ...

  8. EMVS: Event-based Multi-View Stereo 阅读笔记

    0. 摘要 EMVS目的:从已知轨迹的event相机,估计半稠密的3D结构 传统的MVS算法目的:从已知视点的图片集,去估计场景的稠密3D结构. EMVS2个固有属性: (1)   当传感器发生相对运 ...

  9. 电脑上文件的后缀名被隐藏,把一个文本文件改成.bat时,默认打开的还是文本。

    1.打开文件夹,选择组织,点击“文件夹和搜索选项”,如图: 2.选择“查看”,找到“隐藏已知文件类型的扩展名”,不要勾选这一项,如图: 3.点击“确定”或者“应用”

  10. shell脚本,逻辑结构题练习。

    awk '/5/{a=1}!a' file2结果:1234解释:第一行 /5/不匹配跳过{a=1},继续!a,此时a没有值属于假取反为真,故输出第一行 第二行 /5/不匹配跳过{a=1},继续!a,此 ...