0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal
什么是线程池
- 创建线程,因为涉及到跟操作系统交互,比较耗费资源。如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任务传递进去,任务执行完毕不终止线程,等待下一个任务
线程池的种类
- ExecutorService:
- 这是个接口,代表尽快执行的线程池,只要有空闲进程,就立即执行
- Future<?> submit(Runnable task)
- 将Runnable对象提交给线程池,线程池有空闲线程时执行任务,返回的Future对象,因为run()没有返回值,因此实际是null,但可以调用isDone()和isCancelled()方法
- <T> Future <T> submit(Runnable task,T result)
- result是线程执行结束后的返回值
- <T> Future <T> submit(Callable<T> task)
- Future代表Callable的call()方法的返回值
- void shutdown()
- 不再接收新任务,已接收的任务执行完成,然后关闭线程池
- List<Runnable> shutdownNow()
- 停止所有线程任务,并返回等待处理的任务列表
- boolean isShutdown()
- boolean isTerminated()
- shutdown()之后,所有任务都执行完毕,则返回true
- ScheduledExecutorService
- 这也是个接口,代表在指定延迟或者周期性的执行任务的线程池
- ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit)
- Callable任务在delay后执行
- ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
- Runnable任务在delay后执行
- ScheduledFuture<?> scheduleAtFixedTate(Runnable command, long initialDelay, long period, TimeUnit unit)
- 在delay延迟后开始执行,之后周期性(period)执行
- 这里的周期是从上一个任务的
开始时间
开始计算 - 比如:第一次执行任务在1秒处,执行了3秒,第4秒结束,如果设置的period是5秒,那就在第6秒第二次执行该任务
- TimeUnit是个枚举类:可以是天、小时、分钟、秒、毫秒、毫微秒
- ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
- initialDelay后开始执行,执行完毕后,间隔delay后再次执行
- 执行中遇到异常,会终止执行,否则会一直执行,应设立条件终止任务
- 跟上一个方法不同的是,这个方法,第二个周期执行的起算点是第一个周期
结束时间
点
创建线程池
- 通过Executors的静态工厂方法创建线程池
- 创建ExecutorService线程池
- newCachedThreadPool()
- 具有缓存功能的线程池,系统根据需要创建线程,缓存与线程池中
- newFixedThreadPool(int nThreads)
- 可重用的、具有固定线程数的线程池
- newSingleThreadExecutor()
- 单线程的线程池
- newWorkStealingPool(int parallelism)
- 创建以讹持有足够线程的线程池来支持给定的并行级别,并且使用多个队列来减少竞争
- newWorkStealingPool()
- 这个方法是上个方法的简化版,将cpu个数传给上一个方法就是下面这个方法
- newCachedThreadPool()
- 创建ScheduledExecutorService线程池
- newScheduledThreadPool(int corePoolSize)
- corePoolSize是线程数目
- newSingleThreadScheduledExecutor()
- 只有一个线程
- newScheduledThreadPool(int corePoolSize)
示例一:
package testpack;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) throws InterruptedException{
Runnable task=()->{ //创建一个线程任务
for (int i=0;i<1;i++) {
System.out.println(new Date());
try{
Thread.sleep(3000);
}catch(InterruptedException ex){
ex.printStackTrace();
}
System.out.println(new Date());
}
};
ScheduledExecutorService ses=Executors.newScheduledThreadPool(1); //创建一个单线程延迟处理处理线程池,
ses.scheduleAtFixedRate(task,1,5,TimeUnit.SECONDS); //标记㈠。延迟1秒开始处理,从开始处理的时间点开始算,5秒后执行第二个周期
}
}
输出:
Fri Dec 09 16:22:07 CST 2016 //07秒开始处理
Fri Dec 09 16:22:10 CST 2016 //run中暂停3秒
Fri Dec 09 16:22:12 CST 2016 //07+5秒开始第二个周期
Fri Dec 09 16:22:15 CST 2016 //run中暂停3秒
Fri Dec 09 16:22:17 CST 2016 //07+5+5开始第三个周期
示例二:将上面标记㈠的方法改为:scheduleWithFixedDelay,输出如下:
Fri Dec 09 16:28:35 CST 2016 //35秒开始处理
Fri Dec 09 16:28:38 CST 2016 //run中暂停3秒
Fri Dec 09 16:28:43 CST 2016 //38+5秒开始第二个周期
Fri Dec 09 16:28:46 CST 2016 //run中暂停3秒
Fri Dec 09 16:28:51 CST 2016 //46+5秒开始第三个周期
ForkJoinPool
- ForkJoinPool与多CPU、多核CPU计算有关
- 是ExecutorService的实现类,也是一种线程池
- 配合ForkJoinTask完成对一个大任务进行递归拆解成多个小任务并行计算
- 构造器与方法:
- ForkJoinPool(int parallelism)
- 创建一个包含指定个数并行线程的线程池
- ForkJoinPool()
- 创建一个Runtime.availableProcessors()返回值个数的并行线程的线程池
- static ForkJoinPool commenPool()
- 返回一个通用线程池,其运行状态不受shutdown()和shutdownNow()的影响,除非调用System.exit()退出虚拟机
- static int getCommenPoolparallelism()
- 返回通用池的并行级别
- submit(ForkJoinTask task)
- 执行指定任务
- invoke(ForkJoinTask task)
- ForkJoinPool(int parallelism)
- ForkJoinTask
- 这是个抽象类,有RecursiveAction()和RecursiveTask()两个抽象子类,前者无返回值,后者有返回值
- 创建这两类任务时,要继承这两个类,然后重写compute()方法
- 进一步参考:
- 并发编程网
- 《Java并发编程的艺术》第6.4节
- 《Java 7 并发编程实战手册》第5章
- 示例:
package testpack;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) throws InterruptedException{
ForkJoinPool pool=new ForkJoinPool();
pool.submit(new Task(0,327));
pool.awaitTermination(2,TimeUnit.SECONDS);
pool.shutdown();
}
}
class Task extends RecursiveAction{
private static final int THRESHOLD=50;
private int start;
private int end;
public Task(int start,int end){
this.start=start;
this.end=end;
}
protected void compute(){
if (end-start<THRESHOLD) {
for (int i=start;i<end;i++){
System.out.println(Thread.currentThread().getName()+"输出: "+i);
}
}else{
int middle = (start+end)/2;
Task left=new Task(start,middle);
Task right=new Task(middle,end);
left.fork();
right.fork();
}
}
}
ThreadLocal类
- ThreadLocal可以把一个多个线程一起操作的变量包装成一个局部变量,每个线程都拥有一个该变量的副本,各线程对该变量的操作互补影响
- 只有如下三个方法:
- void set(T value)
- T get();
- void remove()
- 各个线程的该局部变量的初始值是null
- 示例
package testpack;
public class Test1 {
public static void main(String[] args){
Account a=new Account("初始名字");
new MyThread(a).start();
new MyThread(a).start();
}
}
class MyThread extends Thread{
private Account account;
public MyThread(Account account){
this.account=account;
}
public void run(){
for (int i=0;i<5;i++) {
if (i==3){
account.setName(Thread.currentThread().getName());
}
System.out.println("线程名称:"+getName()+" ,账户名:"+account.getName()+" 输出:"+i);
}
}
}
class Account {
private ThreadLocal<String> name=new ThreadLocal<>();
public Account(String str){
this.name.set(str);
System.out.println("构造时的账户名:"+this.name.get());
}
public String getName(){
return name.get();
}
public void setName(String str){
this.name.set(str);
}
}
输出:
构造时的账户名:初始名字
线程名称:Thread-0 ,账户名:null 输出:0
线程名称:Thread-1 ,账户名:null 输出:0
线程名称:Thread-0 ,账户名:null 输出:1
线程名称:Thread-1 ,账户名:null 输出:1
线程名称:Thread-0 ,账户名:null 输出:2
线程名称:Thread-1 ,账户名:null 输出:2 //初始值为null
线程名称:Thread-0 ,账户名:Thread-0 输出:3
线程名称:Thread-0 ,账户名:Thread-0 输出:4
线程名称:Thread-1 ,账户名:Thread-1 输出:3
线程名称:Thread-1 ,账户名:Thread-1 输出:4
0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal的更多相关文章
- 0040 Java学习笔记-多线程-线程run()方法中的异常
run()与异常 不管是Threade还是Runnable的run()方法都没有定义抛出异常,也就是说一条线程内部发生的checked异常,必须也只能在内部用try-catch处理掉,不能往外抛,因为 ...
- 0039 Java学习笔记-多线程-线程控制、线程组
join线程 假如A线程要B线程去完成一项任务,在B线程完成返回之前,不进行下一步执行,那么就可以调用B线程的join()方法 join()方法的重载: join():等待不限时间 join(long ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析
JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- java学习笔记14--多线程编程基础1
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...
- 【转】Java学习---深入理解线程池
[原文]https://www.toutiao.com/i6566022142666736131/ 我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很 ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
随机推荐
- 在ASP.NET MVC5中实现具有服务器端过滤、排序和分页的GridView
背景 在前一篇文章<[初学者指南]在ASP.NET MVC 5中创建GridView>中,我们学习了如何在 ASP.NET MVC 中实现 GridView,类似于 ASP.NET web ...
- 每日设置Bing首页图片为壁纸
闲来无事,手痒痒要做一个什么小工具. 于是乎便有了本文. 当有一个想法的时候,首先免不了网上搜索一番以便看一下有木有网友有过类似的想法. 很显然--有! 因此本文大代码是从几个地方搜索,然后组合的. ...
- 在linux平台实现atosl
➠更多技术干货请戳:听云博客 序言 怎么在linux 平台下实现一个类似于mac 平台下的 atos 工具( iOS 符号化解析)? 分析问题 在github上找到了几年前的开源实现,[https:/ ...
- 前端学HTTP之WEB服务器
前面的话 Web服务器每天会分发出数以亿计的Web页面,它是万维网的骨干.本文主要介绍WEB服务器的相关内容 总括 Web服务器会对HTTP请求进行处理并提供响应.术语“Web服务器”可以用来表示We ...
- 检索COM类工厂中CLSID为{00024500-0000-0000-C000-000000000046}的组件时失败
具体解决方法如下: 1:在服务器上安装office的Excel软件: 2:在"开始"->"运行"中输入dcomcnfg.exe启动"组件服务&q ...
- 1.C#面向对象基础简介
学习核心内容: 面向对象的三个特性:封装.继承.多态 访问级别:用处在于控制成员在那些地方可以访问,这样达到面向对象封装的目的. 常用级别:public (任何地方都可以访问) private(默认级 ...
- go语言赋值
使用赋值语句可以更新一个变量的值,最简单的赋值语句是将要被赋值的变量放在=的左边,新值的表达式放在=的右边. x = // 命名变量的赋值 *p = true // 通过指针间接赋值 person.n ...
- 数百个 HTML5 例子学习 HT 图形组件 – 3D 建模篇
http://www.hightopo.com/demo/pipeline/index.html <数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇>里提到 HT 很 ...
- 如果你也会C#,那不妨了解下F#(1):F# 数据类型
本文链接:http://www.cnblogs.com/hjklin/p/fs-for-cs-dev-1.html 简单介绍 F#(与C#一样,念作"F Sharp")是一种基于. ...
- 使用 NuGet 下载最新的 Rafy 框架及文档
为了让开发者更方便地使用 Rafy 领域实体框架,本月,我们已经把最新版本的 Rafy 框架程序集发布到了 nuget.org 上,同时,还把 RafySDK 的最新版本发布到了 VisualStud ...