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

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. java枚举学习入门

    一.前言  在java编程过程中,我们通常需要定义一些固定数量的常量,在jdk1.5以前,通常的做法是定义一个静态常量类,但自jdk1.5后,java引入了枚举(关键字enum,全称为 enumera ...

  2. 寒假短期学习计划 - C++

    寒假短期学习计划 - C++ 一.所选课程 && 相关 0.选以下课的理由: 选课理由0: 只是短期的计划,先选些短视频感受:之后再视情况选其他课: 选课理由1: 难度低,以前自学过一 ...

  3. JS定时器相关用法

    一.定时器在javascript中的作用 1.制作动画 <!DOCTYPE html> <html lang="en"> <head> < ...

  4. oracle 禁用/启动job

    注意:dbms_job只能在job的所在用户使用,如果broken其它用户的job用dbms_ijob dbms_job只能在当期用户内创建job.修改和删除job,不能对其他用户的job进行操作;s ...

  5. ethers.js-4-Contracts

    Contracts A Contract is an abstraction of an executable program on the Ethereum Blockchain. A Contra ...

  6. [译]新的CCSDS图像压缩推荐标准

    摘要——空间数据系统咨询委员会(CCSDS)的数据压缩工作组最近通过了图像数据压缩议案,最终版本预计在2005年发布.议案中采用的算法由两部分组成,先是一个对图像的二维离散小波变换,然后是对变换后的数 ...

  7. ZOJ 2475 Benny's Compiler(dfs判断有向图给定点有没有参与构成环)

    B - Benny's Compiler Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu ...

  8. 查询job的几个语句

    select * from dba_jobs ;select * from dba_scheduler_job_run_details t;    ------>这个语句通过制定job名,来查看 ...

  9. 解决:Cannot load ocl.dll library(error code 126). The ocil.dll library may be missing from the system

           因为这两天在做将springboot 项目使用的数据库MySQL转换为Oracle数据库,所以在网上查找相关资料后开始使用 Convert-Mysql-to-Oracle4.0做转换: ...

  10. OO第一次总结作业

    第一次OO博客作业 前言 面向对象课程已经经过了4周的时间.前三次作业全部是关于多项式求导的相关内容,内容由易到难,同时我也开始逐渐深入感受学习面向对象的各项特征,逐渐将自己的编程风格从C向真正的面向 ...