Java中java.util.concurrent包下的4中线程池代码示例
先来看下ThreadPool的类结构
其中红色框住的是常用的接口和类(图片来自:https://blog.csdn.net/panweiwei1994/article/details/78617117?from=singlemessage)
为什么需要线程池呢?
我们在创建线程的时候,一般使用new Thread(),但是每次在启动一个线程的时候就new 一个Thread对象,会让性能变差(spring不都使用IOC管理对象了嘛)。还有其他的一些弊端:
- 可能会造成无限创建线程对象,对象之间相互竞争资源,造成过多占用资源而宕机。
- 缺乏相关功能,如定时执行、定期执行、线程中断。
使用线程池的避免这些事情:
- 重用存在的线程,减少对象创建、消亡的开销,性能佳。
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
- 提供定时执行、定期执行、单线程、并发数控制等功能。
线程池的种类:
java通过Executor是提供4种线程池,分别为:
1)newCachedThreadPool:创建一个可缓存的线程池,有任务来临时如果线程池中有空闲的线程,那么就使用空闲的线程执行任务(即线程是可以复用),如果没有空闲线程则创建新的线程执行任务。
2)newFixedThreadPool:创建一个定长的线程池,线程池的线程数量固定,当任务来临,但是又没有空闲线程,则把任务放入队列中等待直到有空闲线程来处理它。
3)newScheduledThreadPool:创建一个定长的线程,但是能支持定时或周期性的执行。
4)newSingleThreadPool:创建一个单线程化的线程池,线程池中只有一个唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。
示例:
1)newCachedThreadPool
/**
* 创建可缓存的线程池,线程池的线程可以重复利用,除非任务来不及处理就会创建新的线程。
*/
public static void createCachedThreadPool(){
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
//在这里使主线程停下来,让启动的线程执行完Syso操作
//并且有时间回收线程以确保下次启动的线程还是上次的线程
Thread.sleep(index * 1000);//
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
/* try {
//这里让启动的线程睡眠,保证下次启动的线程是新的线程,不是此时睡眠的。
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName() + " " + index);
}
}); }
}
执行结果:
结果显示,执行for循环输出的线程都是同一个,线程重复使用了。
把注释的地方放开,并且注释上面的睡眠,执行结果:
结果显示的是不同的线程名称执行的for循环,对比上面的执行结果的线程名称,可以得出结论:有任务来临时如果线程池中有空闲的线程,那么就使用空闲的线程执行任务(即线程是可以复用),如果没有空闲线程则创建新的线程执行任务。
2)newFixedThreadPool
/**
* 创建固定长度的线程池,超出的任务会在队列中进行等待,直到有线程空出来来执行。
*/
public static void createFixedThreadPool() {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "," + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
运行结果:
创建了固定长度是3的线程池,输出前3行之后,发现线程都在sleep(),要执行的输出任务没有找到对应的执行线程,任务就会放入队列中进行等待,等待某个线程执行完毕后,再去执行任务。(线程池中的线程也是重复使用的)
3)newScheduledThreadPool
3.1)延迟执行某个线程
public static void createScheduledThreadPoolToDelay(){
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
for(int i = 0; i < 10; i++){
final int index = i;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduledThreadPool.schedule(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + index + " delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
}
执行结果:
延迟3+2秒执行(3秒是newScheduledThreadPool中设置的,2秒是Thread.sleep()设置的),结果中可以看出主线程睡眠2秒并不能保证newScheduledThreadPool线程池中是使用旧线程执行任务还是新建线程执行任务,这种情况是随机的。(这点和newCachedThreadPool不一样,newCachedThreadPool是用就线程)
3.2)定期执行某个任务
public static void createScheduledThreadPoolToFixRate(){
ScheduledExecutorService exe = Executors.newScheduledThreadPool(3);
exe.scheduleAtFixedRate(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS); }
执行结果:
结果是延迟1s启动线程,并且之后每隔3s重复执行任务,但是用的是同一个线程。
4)newSingleThreadPool
/**
* 创建一个单线程的线程池
*/
public static void createSingleThreadPool(){
ExecutorService exe = Executors.newSingleThreadExecutor();
for(int i = 0; i < 10; i++){
final int index = i;
exe.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ", " +index);
Thread.sleep(2000);// 让当前线程睡眠2s,发现顺序打印1~10,并且有个打印停顿2s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
运行结果:
每输出一行结果就等待2s,可以看出每次输出的线程名都一样,说使用的同一个线程。单线程化的线程池中只有一个线程。
总结:
上面就是4中线程池的实现及其使用示例,和他们之间的区别。
其中newFixedThreadPool()有个坑,最好不要使用Executor.newFixedThreadPool(int nThreads)来创建线程池,因为它使用了LinkedBlockingQueue,容量是Integer.MAX_VALUE,容量太大容易造成防止所有任务都被阻塞,从而导致死锁。下面是具体源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
应该尽量直接使用new ThreadPoolExecutor来创建线程池,并指定阻塞队列的容量。
参考文章:https://www.cnblogs.com/zhaoyan001/p/7049627.html
Java中java.util.concurrent包下的4中线程池代码示例的更多相关文章
- java.util.concurrent包下集合类的特点与适用场景
java.util.concurrent包,此包下的集合都不允许添加null元素 序号 接口 类 特性 适用场景 1 Queue.Collection ArrayBlockingQueue 有界.阻塞 ...
- Java并发编程之java.util.concurrent包下常见类的使用
一,Condition 一个场景,两个线程数数,同时启动两个线程,线程A数1.2.3,然后线程B数4.5.6,最后线程A数7.8.9,程序结束,这涉及到线程之间的通信. public class Co ...
- java.util.concurrent包下并发锁的特点与适用场景
序号 类 备注 核心代码 适用场景 1 synchronized 同步锁 并发锁加在方法级别上,如果是单例class对象,则只能允许一个线程进入public synchronized void doX ...
- Java并发机制(8)--concurrent包下辅助类的使用
Java并发编程:concurrent包下辅助类的使用 整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920397.html 1.CountDown ...
- Java中多线程的使用(超级超级详细)线程池 7
Java中多线程的使用(超级超级详细)线程池 7 什么是线程池? 线程池是一个容纳多个线程的容器,线程池中的线程可以重复使用,无需反复创建线程而消耗过多的资源 *使用多线程的好处: 1.降低消耗,减少 ...
- java.util.concurrent包
在JavaSE5中,JUC(java.util.concurrent)包出现了 在java.util.concurrent包及其子包中,有了很多好玩的新东西: 1.执行器的概念和线程池的实现.Exec ...
- java.util.concurrent包API学习笔记
newFixedThreadPool 创建一个固定大小的线程池. shutdown():用于关闭启动线程,如果不调用该语句,jvm不会关闭. awaitTermination():用于等待子线程结束, ...
- 【并发编程】【JDK源码】JDK的(J.U.C)java.util.concurrent包结构
本文从JDK源码包中截取出concurrent包的所有类,对该包整体结构进行一个概述. 在JDK1.5之前,Java中要进行并发编程时,通常需要由程序员独立完成代码实现.当然也有一些开源的框架提供了这 ...
- 高并发编程基础(java.util.concurrent包常见类基础)
JDK5中添加了新的java.util.concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法 ...
随机推荐
- 使用NVM管理Node - Windows
安装 NVM NVM 下载:https://github.com/coreybutler/nvm-windows 安装 Node 注意:如果没有FQ默认源可能安装npm失败,请参考下一节“安装 NPM ...
- Java发送邮件 —— SpringBoot集成Java Mail
用途:此文仅供,自己今后的小程序通过邮件,批量通知用户. 简单记录了一些发送基本邮件的操作. 项目(SpringBoot版本为2.1.2.RELEASE): 核心依赖(其他相关依赖,在其使用的地方具体 ...
- 微信、qq二次分享
前言 我们平时做微信分享的时候,一般分享出来的页面都是一个简单的html页面,不会加入框架之类的东西.所以当我们在分享出来的页面里面再次进行分享的时候,由于我们没有配置分享的标题.描述这些东西,分享出 ...
- web 后台返回json格式数据的方式(status 406)
1.在类上使用注解 @RestController public class HttpComentInterface { } 2.在方法是使用注解 @ResponseBody @RequestMap ...
- 接口测试工具postman
一. 安装 1. 免费官网链接:https://www.getpostman.com/postman,下载好后双击.exe程序安装即可 2. 注意事项:建议安装在非系统盘,即C盘以外的盘,路径最好用全 ...
- 20190411wdVBA_排版
Sub LayoutForExamPaper() Dim StartTime As Variant Dim UsedTime As Variant StartTime = VBA.Timer Appl ...
- 动态嵌套form,使用Stimulus Js库(前后端不分离)
我的git代码:https://github.com/chentianwei411/nested_form-Stimulus- Stimulus: https://www.cnblogs.co ...
- Vue 组件异步加载(懒加载)
一.vue的编译模式 (1)路由配置信息 //eg1: const MSite = resolve => require.ensure([], () =>resolve(require([ ...
- PHP以xml形式获取POST数据
<?php namespace Home\Controller; use Think\Controller; class UrlController extends Controller { / ...
- 【实战问题】【4】Vue写的页面在微信手机端和微信web开发者工具中都能正常显示,但是在微信pc端上显示空白
原因:pc端微信浏览器不支持es6,而代码中使用了 let . 解决:将 let 改为 var(若使用 es6 语法比较多,可以进行转换,将 es6 语法转为 es5) 参考博客: 1,h5微信页面在 ...