线程池本质的概念就是一堆线程一起完成一件事情。

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); 

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返回的线程池对象的弊端如下:

  1. FixedThreadPool和SingleTheadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM;
  2. CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

JUC——线程池的更多相关文章

  1. 【JUC】JUC线程池框架综述

    一.前言 在分析完了JUC的锁和集合框架后,下面进入JUC线程池框架的分析,下面给出JUC线程池的总体框架,之后再逐一进行分析. 二.JUC线程池框架图 说明:从上图可知,JUC线程池框架中的其他接口 ...

  2. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  3. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  4. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  5. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  6. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  7. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  8. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

  9. Java - "JUC线程池" 架构

    Java多线程系列--“JUC线程池”01之 线程池架构 概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介 ...

  10. Java - "JUC线程池" ThreadPoolExecutor原理解析

    Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...

随机推荐

  1. kafka 消费者和生产者测试类

    pom.xml: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www. ...

  2. 那些不明不白的$符号设计--Sass和Emmet,变量设计原理相通

    以前看到php变量的定义,直接使用$符号开始,怎么看都不习惯.后来呀,在使用Emmet的过程中,又接触到了$符号.今天,在学习Sass的过程种,再一次接触到$符号,兴致所致,不由得想写一篇,对比一下搞 ...

  3. linux用户相关及/etc/passed,/etc/group,/etc/shadow

    useradd:新建用户 usermod:修改用户相关信息 userdel:删除用户分(-r选项) 组的操作与用户的操作类似 选项 userdel相关选项: -f:强制删除用户,即使用户已登录 -r: ...

  4. Windows下docker的安装,将ASP.NET Core程序部署在Linux和Docker中

    参考文章: https://www.cnblogs.com/jRoger/p/aspnet-core-deploy-to-docker.html docker for windows下载连接: htt ...

  5. virtualbox+vagrant学习-2(command cli)-21-vagrant up命令

    Up 格式: vagrant up [options] [name|id] 这个命令根据你的Vagrantfile文件创建和配置客户机. 这是“vagrant”中最重要的一个命令,因为它是创建任何va ...

  6. error: OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) in cv::Mat::Mat

    问题原因: You are probably working outside of the image dimensions. Does any of the values you pass to t ...

  7. ansible yaml 语法

    yet another markup language -- 另外的一种标记语言 yaml 语法: 序列:同一个类型的数据有多个,用"-" 来区分,叫序列 映射:价值对,用&quo ...

  8. block本质探寻五之atuto类型局部实例对象

    说明:阅读本文章,请参考之前的block文章加以理解: 一.栈区block分析 //代码 //ARC void test1() { { Person *per = [[Person alloc] in ...

  9. block本质探寻一之内存结构

    一.代码——命令行模式 //main.m #import <Foundation/Foundation.h> struct __block_impl { void *isa; int Fl ...

  10. CANVAS实现调色板 之 我的第一个随笔

    主题代码 <canvas id="color"></canvas> <script> var color=document.getElement ...