java Concurrent包学习笔记(一):ExecutorService
一、介绍
ExecutorService是java.util.concurrent包中的一个线程池实现接口。其有两个实现类:
1)ThreadPoolExecutor:普通线程池通过配置线程池大小,能有效管理线程的调度,在执行大量异步线程时提高程序的性能。
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize:核心线程数,如果运行的线程少于corePoolSize,则创建新线程来执行新任务,即使线程池中的其他线程是空闲的
- maximumPoolSize:最大线程数,可允许创建的线程数,corePoolSize和maximumPoolSize设置的边界自动调整池大小:
- corePoolSize <运行的线程数< maximumPoolSize:仅当队列满时才创建新线程
- corePoolSize=运行的线程数= maximumPoolSize:创建固定大小的线程池
- keepAliveTime:如果线程数多于corePoolSize,则这些多余的线程的空闲时间超过keepAliveTime时将被终止
- unit:keepAliveTime参数的时间单位
- workQueue:保存任务的阻塞队列,与线程池的大小有关:
- 当运行的线程数少于corePoolSize时,在有新任务时直接创建新线程来执行任务而无需再进队列
- 当运行的线程数等于或多于corePoolSize,在有新任务添加时则选加入队列,不直接创建线程
- 当队列满时,在有新任务时就创建新线程
- threadFactory:使用ThreadFactory创建新线程,默认使用defaultThreadFactory创建线程
- handle:定义处理被拒绝任务的策略,默认使用ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException
2)ScheduledThreadPoolExecutor:执行延迟任务和周期性任务。
二、ExecutorService种类
1、newSingleThreadExecutor

由数可知,创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,没有被执行的线程先排在等待队列中,而且先放入线程池的先执行
示例:
package executorservice.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author boshen
* @date 2018/12/20
*/
public class SingleThreadExecutorTest {
class StudentThread implements Runnable{
private String name;
StudentThread(String name){
this.name = name;
}
public void run(){
System.out.println("学生:" + name + " 开始吃饭");
try {
Thread.sleep(3000);
System.out.println("学生:" + name + " 吃完饭了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args){
SingleThreadExecutorTest cb = new SingleThreadExecutorTest();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(cb.new StudentThread("张三"));
executorService.submit(cb.new StudentThread("李四"));
executorService.shutdown();
}
}
学生:张三 开始吃饭
学生:张三 吃完饭了
学生:李四 开始吃饭
学生:李四 吃完饭了
2、newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
示例:
package executorservice.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author boshen
* @date 2018/12/20
*/
public class FixedThreadPoolTest {
class StudentThread implements Runnable{
private String name;
StudentThread(String name){
this.name = name;
}
public void run(){
System.out.println("学生:" + name + " 开始吃饭");
try {
Thread.sleep(2000);
System.out.println("学生:" + name + " 吃完饭了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args){
FixedThreadPoolTest cb = new FixedThreadPoolTest();
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(cb.new StudentThread("张三"));
executorService.submit(cb.new StudentThread("李四"));
executorService.submit(cb.new StudentThread("王五"));
executorService.submit(cb.new StudentThread("马六"));
executorService.shutdown();
}
}
学生:李四 开始吃饭
学生:张三 开始吃饭
学生:李四 吃完饭了
学生:张三 吃完饭了
学生:王五 开始吃饭
学生:马六 开始吃饭
学生:马六 吃完饭了
学生:王五 吃完饭了
3、newCachedThreadPool

创建可缓存的线程池,如果线程池中的线程在60秒未被使用就将被移除,在执行新的任务时,当线程池中有之前创建的可用线程就重用可用线程,否则就新建一条线程
示例:
package executorservice.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author boshen
* @date 2018/12/20
*/
public class CachedThreadPoolTest {
class StudentThread1 implements Runnable{
private String name;
StudentThread1(String name){
this.name = name;
}
public void run(){
System.out.println("学生:" + name + " 开始吃饭,线程名为:"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class StudentThread2 implements Runnable{
private String name;
StudentThread2(String name){
this.name = name;
}
public void run(){
System.out.println("学生:" + name + " 开始吃饭,线程名为:"+Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args){
CachedThreadPoolTest cb = new CachedThreadPoolTest();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(cb.new StudentThread1("张三"));
executorService.submit(cb.new StudentThread1("李四"));
executorService.submit(cb.new StudentThread2("王五"));
executorService.submit(cb.new StudentThread2("马六"));
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.submit(cb.new StudentThread1("赵七"));
executorService.submit(cb.new StudentThread1("杨八"));
executorService.shutdown();
}
}
学生:张三 开始吃饭,线程名为:pool-1-thread-1
学生:王五 开始吃饭,线程名为:pool-1-thread-3
学生:马六 开始吃饭,线程名为:pool-1-thread-4
学生:李四 开始吃饭,线程名为:pool-1-thread-2
学生:赵七 开始吃饭,线程名为:pool-1-thread-2
学生:杨八 开始吃饭,线程名为:pool-1-thread-1
由结果可知:
张三和李四执行时间为2秒,王五和马六执行时间为10秒,提交了前4个线程之后隔了4秒提交赵七和杨八的线程,这时候张三和李四已经执行完了。
所以张三的线程pool-1-thread-1继续执行杨八,李四的线程pool-1-thread-2继续执行赵七。并没有多创建出来pool-1-thread-5和pool-1-thread-6
4、newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行
- Executors.newScheduledThreadPool(int corePoolSize),corePoolSize表示线程容量。
- schedule(Callable/Runnable command,long initialDelay,TimeUnit unit):第一个参数任务,第二个参数表示执行任务前等待的时间,第三参数表示时间单位。
- scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit):第一个参数表示周期线执行的任务,第二个参数表示第一次执行前的延迟时间,第三个参数表示任务启动间隔时间,第四个参数表示时间单位。虽然任务类型是Runnable但该方法有返回值ScheduledFuture。可以通过该对象获取线程信息。
- scheduleWithFixedDelay(Runnable command,long initialDelay,long period,TimeUnit unit):与scheduleAtFixedRate方法类似,不同的是第三个参数表示前一次结束的时间和下一次任务启动的间隔时间
示例
package executorservice.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; /**
* @author boshen
* @date 2018/12/20
*/
public class ScheduledThreadPoolTest {
class StudentThread implements Runnable{
private String name;
StudentThread(String name){
this.name = name;
}
public void run(){
try {
System.out.println("学生:" + name + " 开始吃饭,线程名为:"+Thread.currentThread().getName());
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args){
ScheduledThreadPoolTest cb = new ScheduledThreadPoolTest();
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
//当以下线程提交后要2秒后才执行,只执行一次
executorService.schedule(cb.new StudentThread("张三"),2000, TimeUnit.MILLISECONDS);
//当以下线程提交后要2秒后才执行,每3秒执行一次,直到调用了executorService.shutdown();
executorService.scheduleWithFixedDelay(cb.new StudentThread("李四"),2,3,TimeUnit.SECONDS);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
学生:李四 开始吃饭,线程名为:pool-1-thread-2
学生:张三 开始吃饭,线程名为:pool-1-thread-1
学生:李四 开始吃饭,线程名为:pool-1-thread-2
学生:李四 开始吃饭,线程名为:pool-1-thread-2
学生:李四 开始吃饭,线程名为:pool-1-thread-2
学生:李四 开始吃饭,线程名为:pool-1-thread-2
三、ExecutorService的几个方法区别
1、execute(Runnable),无法取得返回值
public static void main(String[] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
System.out.println("该异步任务无返回值");
}
});
executorService.shutdown();
}
2、submit(Runnable),返回一个 Future 对象。这个 Future 对象可以用来检查 Runnable 是否已经执行完毕,但是也无法取得run方法里面想要返回的值因为run方法为void
public static void main(String[] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Runnable() {
public void run() {
try {
Thread.sleep(10000);
System.out.println("该任务执行了10秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("主线程中获取子线程的执行状态:如果返回null表示执行正确完成");
try {
System.out.println(future.get());//线程没有执行完之前,会阻塞在这里
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
主线程中获取子线程的执行状态:如果返回null表示执行正确完成
该任务执行了10秒
null
3、submit(Callable),返回一个 Future 对象。这个 Future 对象可以返回线程中call方法里面return的对象
public static void main(String[] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Callable() {
public Object call() throws Exception {
Thread.sleep(10000);
System.out.println("该任务执行了10秒");
return "call 返回的值";
}
});
System.out.println("主线程中获取子线程的结果:");
try {
System.out.println(future.get());//线程没有执行完之前,会阻塞在这里
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
主线程中获取子线程的结果:
该任务执行了10秒
call 返回的值
4、invokeAll(Collection<? extends Callable<T>> tasks),参数是加入线程池的所有Callable,返值是List<Future<T>>,表示返回执行后的一系列Callable的执行结果
package executorservice.demo; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* @author Administrator
* @date 2018/12/27
*/
public class InvokeAllTest {
class StudentThread implements Callable{
private String name;
StudentThread(String name){
this.name = name;
}
public Object call() throws Exception{
System.out.println("学生:" + name + " 开始吃饭,线程名为:"+Thread.currentThread().getName());
return "result: 学生"+name+"吃完饭了";
}
}
public static void main(String[] args){
InvokeAllTest invokeAllTest = new InvokeAllTest();
ExecutorService executorService = Executors.newCachedThreadPool();
List<Callable<String>> callables = new ArrayList<Callable<String>>();
callables.add(invokeAllTest.new StudentThread("张三"));
callables.add(invokeAllTest.new StudentThread("李四"));
callables.add(invokeAllTest.new StudentThread("王五"));
callables.add(invokeAllTest.new StudentThread("马六"));
try {
List<Future<String>> futures = executorService.invokeAll(callables);
for(Future<String> future:futures){
System.out.println(future.get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
学生:张三 开始吃饭,线程名为:pool-1-thread-1
学生:王五 开始吃饭,线程名为:pool-1-thread-3
学生:李四 开始吃饭,线程名为:pool-1-thread-2
学生:马六 开始吃饭,线程名为:pool-1-thread-4
result: 学生张三吃完饭了
result: 学生李四吃完饭了
result: 学生王五吃完饭了
result: 学生马六吃完饭了
四、ExecutorService 关闭
ExecutorService 的 shutdown() 方法。并不会立即关闭,但它将不再接受新的任务,而且一旦所有线程都完成了当前任务的时候,ExecutorService 将会关闭。在 shutdown() 被调用之前所有提交给 ExecutorService 的任务都被执行。
如果你想要立即关闭 ExecutorService,你可以调用 shutdownNow() 方法。这样会立即尝试停止所有执行中的任务,并忽略掉那些已提交但尚未开始处理的任务。无法担保执行任务的正确执行。可能它们被停止了,也可能已经执行结束。
java Concurrent包学习笔记(一):ExecutorService的更多相关文章
- java Concurrent包学习笔记(四):BlockingQueue
一.BlockingQueue概述 1.阻塞的含义 BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞.被阻塞的情况主要有如下两种: ,当一个线程对 ...
- java Concurrent包学习笔记(六):Exchanger
一.概述 Exchanger 是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据.这两个线程通过exchange 方法 ...
- java Concurrent包学习笔记(五):Semaphore
一.Semaphore 是什么 信号量Semaphore是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,构造函数初始化的时候可以指定许可的总数量 每次线程执行操作时先通过ac ...
- java Concurrent包学习笔记(三):ReentrantLock
一.可重入性的理解 从名字上理解,ReenTrantLock的字面意思就是再进入的锁,其实synchronized关键字所使用的锁也是可重入的,两者关于这个的区别不大.两者都是同一个线程每进入一次,锁 ...
- java Concurrent包学习笔记(二):CountDownLatch和CyclicBarrier
一.CountDownLatch CountDownLatch一个线程同步的工具,是的一个或者多个线程等待其他线程操作完成之后再执行. CountDownLatch通过一个给定的数值count来进行初 ...
- java Concurrent包学习笔记(七):ConcurrentHashMap
(注意:以下讲解的ConcurrentHashMap是jdk 1.8的) 一.ConcurrentHashMap的数据结构 ConcurrentHashMap在1.8中的实现,相比于1.7的版本基本上 ...
- java.util.concurrent包学习笔记(一)Executor框架
类图: 其实从类图我们能发现concurrent包(除去java.util.concurrent.atomic 和 java.util.concurrent.locks)中的内容并没有特别多,大概分为 ...
- java package 包 学习笔记
编译命令示例: javac -d . Main.java 注:带参数-d自动建立文件目录, 只使用javac 则需要手工创建目录 把 class文件打包 jar命令 jar cvf T.jar *; ...
- java concurrent包的学习(转)
java concurrent包的学习(转) http://my.oschina.net/adwangxiao/blog/110188 我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常 ...
随机推荐
- 【Linux 线程】线程同步《二》
1.读写锁 与互斥量类似,但读写锁允许更高的并行性.其特性为:写独占,读共享. 读写锁状态: 一把读写锁具备三种状态: (1)读模式下加锁状态 (读锁) (2)写模式下加锁状态 (写锁) (3)不加锁 ...
- crm作业知识点集合[三]
知识点1 我们要实现一个这样的功能,在学生表中,可以查看每个学生的报名的班级的所有的成绩,就是下图的效果 1.首先我们需要在学生表中自定义一列,这一列的内容就是一个a标签,指向另外一个页面,而我们在另 ...
- Django的session学习
session的由来 cookie最大的问题就是键值对保存在客户端的浏览器上,我们看下session是如何保存的呢? 比如一个请求过来,数据是这样的{“username”:"zyb" ...
- Django的具体操作(二)
今日内容:用户登录以及分页的实现 views.py # 登录动作 def login_action(request): # 必须继承request if request.method == 'POST ...
- cloud配置中心遇到的坑
https://blog.csdn.net/z960339491/article/details/80593982分布式配置中心为什么要有用分布式配置中心这玩意儿?现在这微服务大军已经覆盖了各种大小型 ...
- go语言中的文件创建,写入,读取,删除
package main; import ( "os" "fmt" "strconv" ) func main() { //打开文件,返回文 ...
- JFinal Model判断数据库某条记录的属性字段是否包含空值
如果做报表,一条记录中有空值,使用FreeMarker渲染word会报错,并把错误日志输出到Word中.所以需要之前判断下当前记录中属性值是否有空值. package com.huijiasoft.u ...
- Oracle性能优化4-索引
Oracle优化可以分为通过改写sql优化和不改写sql优化不改写sql优化一般通过index来实现 在Oracle数据库中,索引按照索引机制的不同,可以分为三种. 1. B-Tree索引 B-Tre ...
- (转)easyui datagrid 部分参数说明
easyui datagrid 部分参数 数据表格属性(DataGrid Properties) 属性继承控制面板,以下是数据表格独有的属性. 名称 类型 描述 默认值 columns array 数 ...
- Site.ForProductsOfApple
1. cultofmac http://www.cultofmac.com/ 2. imore http://www.imore.com/ 3. osxdaily http://osxdaily.co ...