一、概述

  在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:

  以下方法是Executors下的静态方法,Executors中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。

  Executors只是一个工厂类,它所有的方法返回的都是ThreadPoolExecutorScheduledThreadPoolExecutor这两个类的实例。一共可以创建四种线程池。

1.1、基础类

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit; public class TestThread implements Runnable {
// 线程私有属性,创建线程时创建
private Integer num = 0; public TestThread(Integer num) {
this.num = num;
} @Override
public void run() {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("thread:" + Thread.currentThread().getName() + ",time:" + sdf1.format(new Date()) + ",num:" + num);
try {
//使线程睡眠,模拟线程阻塞情况
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+num+"执行完毕");
}
}

0、使用ThreadPoolExecutor 创建使用

    public static void testThreadPoolExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5));
for (int i = 0; i < 15; i++) {
TestThread myTask = new TestThread(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
}
executor.shutdown();
}

输出:

/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=54880:/Applications/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/lib/tools.jar:/Users/lihongxu6/IdeaProjects/java-data-structure-algorithm/data-005-jdkstruct/target/classes:/Users/lihongxu6/.m2/repository/junit/junit/4.11/junit-4.11.jar com.github.bjlhx15.datastructure.algorithm.thread.ThreadPoolDemo2
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完的任务数目:0
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完的任务数目:0
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:6,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:7,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:8,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:9,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:10,队列中等待执行的任务数目:5,已执行完的任务数目:0
thread:pool-1-thread-6,time:2019-06-19 06:10:53,num:10
thread:pool-1-thread-2,time:2019-06-19 06:10:53,num:1
thread:pool-1-thread-7,time:2019-06-19 06:10:53,num:11
thread:pool-1-thread-5,time:2019-06-19 06:10:53,num:4
thread:pool-1-thread-9,time:2019-06-19 06:10:53,num:13
thread:pool-1-thread-8,time:2019-06-19 06:10:53,num:12
thread:pool-1-thread-4,time:2019-06-19 06:10:53,num:3
thread:pool-1-thread-10,time:2019-06-19 06:10:53,num:14
thread:pool-1-thread-1,time:2019-06-19 06:10:53,num:0
thread:pool-1-thread-3,time:2019-06-19 06:10:53,num:2
task 11执行完毕
task 14执行完毕
task 12执行完毕
task 0执行完毕
task 2执行完毕
task 3执行完毕
task 13执行完毕
task 1执行完毕
task 10执行完毕
task 4执行完毕
thread:pool-1-thread-3,time:2019-06-19 06:10:56,num:9
thread:pool-1-thread-1,time:2019-06-19 06:10:56,num:8
thread:pool-1-thread-10,time:2019-06-19 06:10:56,num:7
thread:pool-1-thread-8,time:2019-06-19 06:10:56,num:6
thread:pool-1-thread-7,time:2019-06-19 06:10:56,num:5
task 8执行完毕
task 5执行完毕
task 7执行完毕
task 9执行完毕
task 6执行完毕 Process finished with exit code 0

1.2、线程池的submit和execute方法区别

1、接收的参数不一样

2、submit有返回值,而execute没有

3、submit方便Exception处理
意思就是如果你在你的task里会抛出checked或者unchecked exception,
而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。

1.3、用法统一说明

理解:001-多线程-基础-进程线程、线程状态、优先级、用户线程和守护线程

  java中线程分为两种类型:用户线程和守护线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.setDaemon(true)设置为守护线程。如果不设置此属性,默认为用户线程
  用户线程和守护线程的区别:【用户线程存活程序存活,其他则结束

故创建后会等待结果

注意点一、单元测试,如果不加Thread.sleep(3000)会直接退出,或者没有Future get 也会直接退出

  用户线程为什么直接退出?

  单元测试核心类:TestRunner

public class TestRunner extends BaseTestRunner {
private ResultPrinter fPrinter;
public static final int SUCCESS_EXIT = 0;
public static final int FAILURE_EXIT = 1;
public static final int EXCEPTION_EXIT = 2;
//……
public static void main(String[] args) {
TestRunner aTestRunner = new TestRunner();
try {
TestResult r = aTestRunner.start(args);
if (!r.wasSuccessful()) {
System.exit(1);
}
System.exit(0);
} catch (Exception var3) {
System.err.println(var3.getMessage());
System.exit(2);
}
}

  查看TestResult

    /**
* Returns whether the entire test was successful or not.
* 返回整个测试是否成功。
*/
public synchronized boolean wasSuccessful() {
return failureCount() == 0 && errorCount() == 0;
}

  可以看到:当aTestRunner调用start方法后不会去等待子线程执行完毕在关闭主线程,而是直接调用TestResult.wasSuccessful()方法,而这个方法始终返回的是false,所以主线程接下来就会执行System.exit,这个放回会结束当前运行的jvm虚拟机,所以使用junit测试多线程方法的结果异常就正常了;

  想要正常输出的话可以让主线程不要结束,等待子线程全部运行结束后在结束主线程,输出结果就会正常。

  使用:1、Thread.sleep(2000);或者 2、future.get()阻塞 或者 3、直接使用main方法测试

注意点二、方法内调用多线程方法,方法结束,多线程不结束

web中Controller请求

    //容器主线程持续运行,程序会立即返回,然后后台默默执行开启的线程
@RequestMapping(value = "/poolSubmit", method = RequestMethod.GET)
@ResponseBody
public Object poolSubmit() {
for (int i = 0; i < 10; i++) {
fixedThreadPool.execute(() -> {
try {
Thread.sleep(2000);
System.out.println(new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
fixedThreadPool.shutdown();
return "ok";
}

  上述可能拿不到结果,如果需要结果,推荐使用submit带返回Future的get阻塞方式,如下

    //容器主线程 持续运行,程序阻塞
@RequestMapping(value = "/poolSubmitGet", method = RequestMethod.GET)
@ResponseBody
public Object poolSubmitGet() {
List<Future<String>> futureList = new ArrayList<>();
for (int i = 0; i < 15; i++) {
Future<String> submit = fixedThreadPool.submit(() -> {
try {
System.out.println("======-----------------------------" + new Date());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok:" + new Date();
});
futureList.add(submit);
} for (Future<String> future : futureList) {
try {
System.out.println(future.get() + "ok");
} catch (Exception e) {
e.printStackTrace();
}
}
return "ok";
}

  相当于:便于理解web请求

    public static void main(String[] args) {
System.out.println(getTest());
//程序立即打印 Ok
//同时 都是用户线程 会等方法中的全部线程执行完毕后退出
}
public static String getTest(){
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
fixedThreadPool.submit(()->{
try { Thread.sleep(2000);
System.out.println(new Date()+":"+Thread.currentThread().getName()
+";守护线程:"+Thread.currentThread().isDaemon());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
fixedThreadPool.shutdown();
return "ok";
}

二、内置工具类库

2.1、newFixedThreadPool  //创建固定容量大小的缓冲池

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
  newFixedThreadPool返回一个包含指定数目【固定大小】线程的线程池ThreadPool,如果任务数量多于线程数目,那么没有没有执行的任务必须等待,直到有任务完成为止。
  newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
  示例:
    public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 50; i++) {
fixedThreadPool.submit(new TestThread((i + 1)));
}
fixedThreadPool.shutdown();

2、newCachedThreadPool //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE,空闲60s销毁

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}  

  newCachedThreadPool比较适合没有固定大小并且比较快速就能完成的小任务,没必要维持一个Pool,这比直接new Thread来处理的好处是能在60秒内重用已创建的线程。
  其他类型的ThreadPool看看构建参数再结合上面所说的特性就大致知道它的特性。该线程池不会对线程数目加以限制,完全依赖于JVM能创建线程的数量,可能引起内存不足。

  newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

    public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
cachedThreadPool.submit(new TestThread((i + 1)));
}
cachedThreadPool.shutdown();
}

3、newSingleThreadExecutor //创建容量为1的缓冲池

    public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}

  newSingleThreadExecutor返回以个包含单线程的Executor,将多个任务交给此Exector时,这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

  从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

  实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。

  另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。

    public static void main(String[] args) {
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 50; i++) {
singleThreadPool.submit(new TestThread((i + 1)));
}
singleThreadPool.shutdown();
}

4、newScheduledThreadPool

可调度线程池

    public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//定时
// scheduledThreadPool.schedule(new Runnable() {
// @Override
// public void run() {
// SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// System.out.println("thread:" + Thread.currentThread().getName() + ",time:" + sdf1.format(new Date()));
// }
// }, 3, TimeUnit.SECONDS); //循环周期执行
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
}

003-多线程-JUC线程池-几种特殊的ThreadPoolExecutor【newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool】的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 12 Windows编程——子窗口和系统内置窗口类“BUTTON”

    创建子窗口类,使得子窗口有自己的处理过程. 子窗口类型WS_CHILD不能和WS_POPUP一起使用!为什么子窗口要有自己的处理过程?如果使用主窗口类来创建子窗口,那么子窗口和主窗口将公用窗口处理过程 ...

  2. Linux学习笔记(十二)VIM编辑器

    一.概述 VI Visual interface 可视化接口,类似于Windows中的记事本 VI->VIM 操作模式: (1)Command mode 命令模式 (2)Insert mode ...

  3. PAT Basic 1089 狼人杀-简单版 (20 分)

    以下文字摘自<灵机一动·好玩的数学>:“狼人杀”游戏分为狼人.好人两大阵营.在一局“狼人杀”游戏中,1 号玩家说:“2 号是狼人”,2 号玩家说:“3 号是好人”,3 号玩家说:“4 号是 ...

  4. layer学习

    layer版本v2.1 1,layer的alert可以传标题的: layer.alert("测试layer弹窗===========", {title:"温馨提示&quo ...

  5. 算法笔记--可撤销并查集 && 可持久化并查集

    可撤销并查集模板: struct UFS { stack<pair<int*, int>> stk; int fa[N], rnk[N]; inline void init(i ...

  6. 2018年6月2日-徐州ICPC邀请赛日志-收获第一枚icpc奖牌

    2018年徐州ICPC邀请赛日志 Z的预言成真了,在正式比赛的前一天他的说说是“last one”,没错正赛后就是铜牌区的最后一名.最后揭榜前的15分钟,我们三个如坐针毡,最后奇迹诞生了!       ...

  7. HTTP通过Get请求传递参数时特殊字符被转码的处理方式

    有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了. 编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值.例如 ...

  8. Lua 学习之基础篇九<Lua 协同程序(Coroutine)>

    引言 讲到协程,首先来介绍一下线程和协程的区别 lua协程和多线程 相同之处:拥有自己独立的桟.局部变量和PC计数器,同时又与其他协程共享全局变量和其他大部分东西 不同之处:一个多线程程序可以同时运行 ...

  9. 利用python pika库实现rabbitmq客户端

    pika 实现consumer import functools import logging import pika LOG_FORMAT = ('%(levelname) -10s %(ascti ...

  10. 用Qt 画一个心形

    MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTi ...