Java中常用的四种线程池
在Java中使用线程池,可以用ThreadPoolExecutor的构造函数直接创建出线程池实例,如何使用参见之前的文章Java线程池构造参数详解。不过,在Executors类中,为我们提供了常用线程池的创建方法。接下来我们就来了解常用的四种:
newFixedThreadPool
首先,看一下这种线程池的创建方法:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
从构造方法可以看出,它创建了一个固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大值nThreads。线程池的大小一旦达到最大值后,再有新的任务提交时则放入无界阻塞队列中,等到有线程空闲时,再从队列中取出任务继续执行。
那么,如何使用newFixedThreadPool呢?我们来举个例子:
public class OneMoreStudy {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat(
"HH:mm:ss");
System.out.println("运行时间: " +
sdf.format(new Date()) + " " + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
fixedThreadPool.shutdown();
}
}
上面的例子中创建了一个固定大小为3的线程池,然后在线程池提交了5个任务。在提交第4个任务时,因为线程池的大小已经达到了3并且前3个任务在运行中,所以第4个任务被放入了队列,等待有空闲的线程时再被运行。运行结果如下(注意前3个任务和后2个任务的运行时间):
运行时间: 08:09:02 1
运行时间: 08:09:02 2
运行时间: 08:09:02 0
运行时间: 08:09:04 4
运行时间: 08:09:04 3
newCachedThreadPool
首先,看一下这种线程池的创建方法:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
从构造方法可以看出,它创建了一个可缓存的线程池。当有新的任务提交时,有空闲线程则直接处理任务,没有空闲线程则创建新的线程处理任务,队列中不储存任务。线程池不对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。如果线程空闲时间超过了60秒就会被回收。
那么,如何使用newCachedThreadPool呢?我们来举个例子:
public class OneMoreStudy {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
final int index = i;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat(
"HH:mm:ss");
System.out.println("运行时间: " +
sdf.format(new Date()) + " " + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
cachedThreadPool.shutdown();
}
}
因为这种线程有新的任务提交,就会创建新的线程(线程池中没有空闲线程时),不需要等待,所以提交的5个任务的运行时间是一样的,运行结果如下:
运行时间: 08:45:18 2
运行时间: 08:45:18 1
运行时间: 08:45:18 3
运行时间: 08:45:18 4
运行时间: 08:45:18 0
newSingleThreadExecutor
首先,看一下这种线程池的创建方法:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
从构造方法可以看出,它创建了一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
那么,如何使用newSingleThreadExecutor呢?我们来举个例子:
public class OneMoreStudy {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat(
"HH:mm:ss");
System.out.println("运行时间: " +
sdf.format(new Date()) + " " + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
singleThreadExecutor.shutdown();
}
}
因为该线程池类似于单线程执行,所以先执行完前一个任务后,再顺序执行下一个任务,
运行结果如下:
运行时间: 08:54:17 0
运行时间: 08:54:19 1
运行时间: 08:54:21 2
运行时间: 08:54:23 3
运行时间: 08:54:25 4
有的同学可能会质疑:既然类似于单线程执行,那么这种线程池还有存在的必要吗?这里的单线程执行指的是线程池内部,从线程池外的角度看,主线程在提交任务到线程池时并没有阻塞,仍然是异步的。
newScheduledThreadPool
这个方法创建了一个固定大小的线程池,支持定时及周期性任务执行。
首先看一下定时执行的例子:
public class OneMoreStudy {
public static void main(String[] args) {
final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("提交时间: " + sdf.format(new Date()));
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("运行时间: " + sdf.format(new Date()));
}
}, 3, TimeUnit.SECONDS);
scheduledThreadPool.shutdown();
}
}
使用该线程池的schedule方法,延迟3秒钟后执行任务,运行结果如下:
提交时间: 09:11:39
运行时间: 09:11:42
再看一下周期执行的例子:
public class OneMoreStudy {
public static void main(String[] args) {
final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
System.out.println("提交时间: " + sdf.format(new Date()));
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("运行时间: " + sdf.format(new Date()));
}
}, 1, 3, TimeUnit.SECONDS);
Thread.sleep(10000);
scheduledThreadPool.shutdown();
}
}
使用该线程池的scheduleAtFixedRate方法,延迟1秒钟后每隔3秒执行一次任务,运行结果如下:
提交时间: 09:23:20
运行时间: 09:23:21
运行时间: 09:23:24
运行时间: 09:23:27
Java中常用的四种线程池的更多相关文章
- Java通过Executors提供四种线程池
http://cuisuqiang.iteye.com/blog/2019372 Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果 ...
- java自带的四种线程池
java预定义的哪四种线程池? newSingleThreadExexcutor:单线程数的线程池(核心线程数=最大线程数=1) newFixedThreadPool:固定线程数的线程池(核心线程数= ...
- Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...
- Java四种线程池
Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...
- Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() { @Override public void ru ...
- Java 四种线程池的用法分析
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...
- Java四种线程池的学习与总结
在Java开发中,有时遇到多线程的开发时,直接使用Thread操作,对程序的性能和维护上都是一个问题,使用Java提供的线程池来操作可以很好的解决问题. 一.new Thread的弊端 执行一个异步任 ...
- Java ExecutorService四种线程池及自定义ThreadPoolExecutor机制
一.Java 线程池 Java通过Executors提供四种线程池,分别为:1.newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收 ...
- Java四种线程池的使用
Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...
随机推荐
- Spring Cloud异步场景分布式事务怎样做?试试RocketMQ
一.背景 在微服务架构中,我们常常使用异步化的手段来提升系统的 吞吐量 和 解耦 上下游,而构建异步架构最常用的手段就是使用 消息队列(MQ),那异步架构怎样才能实现数据一致性呢?本文主要介绍如何使用 ...
- Jenkins 结合 Docker 为 .NET Core 项目实现低配版的 CI&CD
随着项目的不断增多,最开始单体项目手动执行 docker build 命令,手动发布项目就不再适用了.一两个项目可能还吃得消,10 多个项目每天让你构建一次还是够呛.即便你的项目少,每次花费在发布上面 ...
- .netCore+Vue 搭建的简捷开发框架 (4)--NetCore 基础
书接上文:上一节中,我们已经实现Services 层.(https://www.cnblogs.com/xuzhencheng/p/11424751.html) 但是具体要如何将服务依赖注入进来呢?继 ...
- 基于操作系统原理的Linux 的基本操作和常用命令的使用
一.实验目的 1.学会不同Linux用户登录的方法. 2.掌握常用Linux命令的使用方法. 3.了解Linux命令中参数选项的用法和作用. 二.实验内容 1. 文件操作命令 (1) 查看文件与目录 ...
- hadoop之mapreduce详解(优化篇)
一.概述 优化前我们需要知道hadoop适合干什么活,适合什么场景,在工作中,我们要知道业务是怎样的,能才结合平台资源达到最有优化.除了这些我们当然还要知道mapreduce的执行过程,比如从文件的读 ...
- Spring boot 梳理 - 配置eclipse集成maven,并开发Spring boot hello
@RestController @EnableAutoConfiguration public class App { @RequestMapping("/hello") publ ...
- Spring boot 官网学习笔记 - Spring Boot CLI 入门案例
安装CLI https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.1.1.RELEASE/spring-b ...
- Navicat 设置自增长初始值
设置主键自增初始值
- Flask基础(11)-->http的无状态协议解决办法二(服务端session)
session的交互流程 Session 对于敏感.重要的信息,建议存在服务器端,不能存储在浏览器中,比如用户名,密码,余额,验证码等信息 在服务器端进行状态保持的方案就是Session Sessio ...
- .netcore+vue+elementUI 前后端分离---支持前端、后台业务代码扩展的快速开发框架
框架采用.NetCore + Vue前后端分离,并且支持前端.后台代码业务动态扩展,框架内置了一套有着20多种属性配置的代码生成器,可灵活配置生成的代码,代码生成器界面配置完成即可生成单表(主表)的增 ...