JUC——线程池
线程池本质的概念就是一堆线程一起完成一件事情。
Executor
package java.util.concurrent;
public interface Executor {
void execute(Runnable command);
}
ExecutorService
package java.util.concurrent;
public interface ExecutorService extends Executor
ScheduledExecutorService
package java.util.concurrent;
public interface ScheduledExecutorService extends ExecutorService
Executors
package java.util.concurrent;
public class Executors
线程池分类
创建线程池使用类:java.util.concurrent.Executors
- 创建无大小限制的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} - 创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
} - 创建单线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
} - 创建定时调度池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
创建四种线程池
当Executors创建完成了线程池之后可以返回“ExecutorService”接口对象,而这个对象里面有两个方法来接收线程的执行:
接收Callable:
public <T> Future<T> submit(Callable<T> task);
接收Runnable:
public Future<?> submit(Runnable task);
范例:创建无限量线程池
package so.strong.mall.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool(); //创建一个线程池
for (int i = 0; i < 5; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行操作");
}
});
}
service.shutdown(); //线程池执行完毕后需要关闭
}
}
无限量大小的线程池会根据内部线程的执行状况来进行线程对象个数的控制。
范例:创建有限量的线程池
package so.strong.mall.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3); //创建一个线程池
for (int i = 0; i < 5; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行操作");
}
});
}
service.shutdown(); //线程池执行完毕后需要关闭
}
}
范例:创建单线程池
package so.strong.mall.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor(); //创建一个线程池
for (int i = 0; i < 5; i++) {
service.submit(new Runnable() { //线程池会负责启动
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行操作");
}
});
}
service.shutdown(); //线程池执行完毕后需要关闭
}
}
除了以上的三种线程池之外还可以创建一个定时调度池,这个调度池主要是以时间间隔调度为主。如果要创建调度池则使用ScheduledExecutorService接口完成,该接口之中包含有如下的两个方法:
- 延迟启动
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
- 间隔调度
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
范例:创建调度池
package so.strong.mall.concurrent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; public class ExecutorDemo {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
for (int i = 0; i < 5; i++) {
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行操作");
}
},2, TimeUnit.SECONDS);
}
service.shutdown();
}
}
范例:观察间隔调度
package so.strong.mall.concurrent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; public class ExecutorDemo {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
for (int i = 0; i < 5; i++) {
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行操作");
}
},2,3, TimeUnit.SECONDS);
}
}
}
ExecutorService线程池处理方法
通过之前的线程池的演示可以发现,整个线程池的处理里面都是以ExecutorService接口的方法为核心展开的。所以如果要想理解线程池,还需要对整个接口的方法做一个小小的说明。
1、在Executor接口里面定义了有execute()方法:
public interface Executor {
void execute(Runnable command);
}
这个方法接收的是Runnable,因为Runnable没有返回值,所以该方法的返回值为void。
范例:使用execute()方法来代替之前的submit():
package so.strong.mall.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行操作");
}
});
}
service.shutdown();
}
}
2、在ExecutorService接口里面的确提供有接收Runnable接口对象的方法,但是这个方法为了统一使用的是submit()。submit()重载了许多次,可以接收Runnable:
public Future<?> submit(Runnale task)
范例:使用ExecutorService接口的submit:
package so.strong.mall.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
Future<?> future = service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行操作");
}
});
System.out.println(future.get()); //Runnable接口没有返回值,所以永恒为null
}
service.shutdown();
}
}
3、 submit()方法是可以接收Callable接口对象的
package so.strong.mall.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
Future<?> future = service.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
return Thread.currentThread().getName() + "执行操作";
}
});
System.out.println(future.get());
}
service.shutdown();
}
}
查看execute()方法的源码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
在这个方法里面主要区分三个概念:
- task:是具体的线程执行任务,线程在追加线程池的时候没有进行启动;
- worker:任务的执行需要worker来支持的,可以运行的worker受到“corePoolSize”限制;
- reject:如果现在线程池已经满了或者关闭了,那么就会出现拒绝新线程加入的可能性。
4、Future线程模型设计的优势在于:可以进行线程数据的异步控制,但是在之前编写的过程严格来讲并不好,相当于启动了一个线程就获得了一个返回值,于是为了方便这些线程池中线程对象的管理,可以使用如下方法进行统一返回:
public interface ExecutorService extends Executor {
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
}
范例:使用invokeAny()方法
package so.strong.mall.concurrent;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
Set<Callable<String>> tasks = new HashSet<>(); //所有任务
for (int i = 0; i < 10; i++) {
final int temp = i;
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "执行任务,i=" + temp;
}
});
}
ExecutorService service = Executors.newCachedThreadPool(); //创建一个线程池
String invokeAny = service.invokeAny(tasks); //执行任务
System.out.println("返回结果:" + invokeAny);
service.shutdown(); }
}
返回结果:pool-1-thread-2执行任务,i=4
使用invokeAny()方法只会返回一个任务的执行操作
5、对于多个线程任务的执行结果,可以以list形式返回
public interface ExecutorService extends Executor {
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
}
范例:使用invokeAll()方法
package so.strong.mall.concurrent;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
Set<Callable<String>> tasks = new HashSet<>(); //所有任务
for (int i = 0; i < 10; i++) {
final int temp = i;
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "执行任务,i=" + temp;
}
});
}
ExecutorService service = Executors.newCachedThreadPool(); //创建一个线程池
List<Future<String>> invokeAll = service.invokeAll(tasks); //执行任务
for (Future<String> future : invokeAll) {
System.out.println("返回结果:" + future.get());
}
service.shutdown(); }
}
CompletionService线程池异步交互
package java.util.concurrent;
public interface CompletionService<V> {
Future<V> submit(Callable<V> task);
Future<V> submit(Runnable task, V result);
Future<V> take() throws InterruptedException;
Future<V> poll();
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
线程池异步交互:CompletionService
- 将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者submit()执行的任务,使用者take()已完成的任务,并按照完成任务的顺序处理它们的结果。
- CompletionService依赖于一个单独的Executor来实际执行任务,在这种情况下,CompletionService只管理一个内部完成队列,在CompletionService接口里面提供有如下两个方法:
- 设置Callable:
public Future<V> submit(Callable<V> task);
- 设置Runnable:
public Future<V> submit(Runnable task, V result);
- 设置Callable:
CompletionService是一个接口,如果要想使用这个接口可以采用ExecutorCompletionService这个子类
public class ExecutorCompletionService<V> implements CompletionService<V>
ExecutorCompletionService的构造方法:
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
CompletionService来控制所有线程池的操作以及数据返回,则应该使用这个类来进行线程池的提交处理。
- 提交线程
Future<V> submit(Callable<V> task);
- 获取返回内容
Future<V> take() throws InterruptedException;
范例:使用CompletionService工具类
package so.strong.mall.concurrent;
import java.util.concurrent.*; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newCachedThreadPool();
CompletionService<String> completions = new ExecutorCompletionService<>(service);
for (int i = 0; i < 5; i++) {
final int temp = i;
completions.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName() + "- i =" + temp;
}
});
}
for (int i = 0; i < 5; i++) {
System.out.println(completions.take().get());
}
service.shutdown();
}
}
CompletionService操作接口的主要目的是可以去隐藏ExecutorService接口执行线程池的处理,不在需要关心novkeAny(), invokeAll()的执行方法了。
ThreadPoolExecutor线程池执行者
线程池的创建主要依靠的是一个类完成的:ThreadPoolExecutor,下面以无限量线程池为例做一个说明:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
观察ThreadPoolExecutor类的构造方法:
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;
}
范例:创建属于自己的线程池
package so.strong.mall.concurrent;
import java.util.concurrent.*; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 5, 6L, TimeUnit.SECONDS, queue);
for (int i = 0; i < 2; i++) {
final int temp = i;
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("[" + Thread.currentThread().getName() + "]任务开始执行-" + temp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]任务结束执行-" + temp);
}
});
}
pool.shutdown();
}
}
在线程池中存在拒绝策略的概念,所谓的拒绝策略指的是线程池满了之后的其他等待线程的处理状态。在ThreadPoolExecutor类里面提供有一些“RejectedExecutorHandler”子类。如果现在被拒绝会出现“拒绝异常”(默认AbortPolicy)、对于给出的几种拒绝策略如下:
- AbortPolicy:默认实现,当任务添加到线程池中被拒绝时,会抛出拒绝异常"RejectedExecutionException";
- DiscardPolicy:当将任务添加到线程池中被拒绝的时候,线程池将直接丢弃该拒绝的任务;
- DiscardOldestPolicy:当被拒绝时,线程池会放弃等待队列中时间最长的未被处理的任务,然后将被拒绝的任务添加到队列之中;
- CallerRunsPolicy:当任务被拒绝的时候,会在线程池当前正在运行的Thread线程之中处理该任务(加塞)。
范例:修改一下侧率
package so.strong.mall.concurrent;
import java.util.concurrent.*; public class ExecutorDemo {
public static void main(String[] args) throws Exception {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 6L, TimeUnit.SECONDS, queue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
final int temp = i;
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("[" + Thread.currentThread().getName() + "]任务开始执行-" + temp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]任务结束执行-" + temp);
}
});
}
pool.shutdown();
}
}
Exception in thread "main" [pool-1-thread-1]任务开始执行-0
[pool-1-thread-2]任务开始执行-3
java.util.concurrent.RejectedExecutionException: Task so.strong.mall.concurrent.ExecutorDemo$1@6da13f3d rejected from java.util.concurrent.ThreadPoolExecutor@45d45d18[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
at so.strong.mall.concurrent.ExecutorDemo.main(ExecutorDemo.java:15)
由于只设置了一个CorePoolSize,所以当多余的任务出现之后将采用设置的默认的拒绝策略:“new ThreadPoolExecutor.AbortPolicy()”会出现“RejectedExecutionException"异常,如果出现拒绝之后丢弃该拒绝的任务,那么也可以将拒绝策略修改为:
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 6L, TimeUnit.SECONDS, queue, Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
对于拒绝策略最简单的解释就是:线程池中的corePoolSize满了,再追加的任务的处理方案。
PS:阿里巴巴Java开发手册——并发处理第4条
【强制】线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式创建,这样的处理方式能让编程代码的工程师更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
- FixedThreadPool和SingleTheadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM;
- CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
JUC——线程池的更多相关文章
- 【JUC】JUC线程池框架综述
一.前言 在分析完了JUC的锁和集合框架后,下面进入JUC线程池框架的分析,下面给出JUC线程池的总体框架,之后再逐一进行分析. 二.JUC线程池框架图 说明:从上图可知,JUC线程池框架中的其他接口 ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- java多线程系类:JUC线程池:02之线程池原理(一)
在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC线程池”02之 线程池原理(一)
概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- Java多线程系列--“JUC线程池”05之 线程池原理(四)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- Java - "JUC线程池" 架构
Java多线程系列--“JUC线程池”01之 线程池架构 概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介 ...
- Java - "JUC线程池" ThreadPoolExecutor原理解析
Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...
随机推荐
- Log4net 使用之 自定义字段
Log4net 是.Net下一个非常优秀的开源日志记录组件.Log4net记录日志的功能非常强大.它可以将日志分不同的等级,以不同的格式,输出到不同的媒介. 由于业务需要,计划为日志增加2个字段,除了 ...
- echo,die(),print(),print_r(),var_dump()的区别
echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用) print() 只能打印出简单类型变量的值(如int,string) 例子 输出 3 ...
- 2040-亲和数(java)
http://acm.hdu.edu.cn/showproblem.php?pid=2040 import java.util.Scanner; public class Main{ public s ...
- BZOJ1001: [BeiJing2006]狼抓兔子【最短路+对偶图】
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1001 1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Se ...
- Java设计模式16:常用设计模式之观察者模式(行为型模式)
1. Java之观察者模式(Observer Pattern) (1)概述: 生活中我们在使用新闻app,当我们对某一栏比较感兴趣,我们往往会订阅这栏新闻,比如我对军事栏感兴趣,我就会订阅军事栏的新闻 ...
- BZOJ1434:[ZJOI2009]染色游戏(博弈论)
Description 一共n×m个硬币,摆成n×m的长方形.dongdong和xixi玩一个游戏,每次可以选择一个连通块,并把其中的硬币全部翻转,但是需要满足存在一个硬币属于这个连通块并且所有其他硬 ...
- logstash 匹配日志格式
2017-05-15 12:06:17 INFO me.cinyi.imapp.push.commons.iospush - 用户ID[1000]-标识[11500], admin推送通知成功, ...
- Vue滚动加载自定义指令
用Vue在移动端做滚动加载,使用mint-ui框架, InfiniteScroll指令loadmore组件,在uc浏览器和qq浏览器都无法触发.无奈我只能自己写了. 决定用vue 的自定义指令 写滚动 ...
- Elasticsearch之如何合理分配索引分片
大多数ElasticSearch用户在创建索引时通用会问的一个重要问题是:我需要创建多少个分片? 在本文中, 我将介绍在分片分配时的一些权衡以及不同设置带来的性能影响. 如果想搞清晰你的分片策略以及如 ...
- webpack一小时入门
什么是 webpack? webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX).coffee.样式(含less/sass).图片等都作为模块来使用和处理. 我们可以 ...