1.java多线程编程少不了使用线程池,线程池相关的工具类所在jdk包,java.util.concurrent

2.使用示例

demo1

public class ThreadPoolDemo {
/*
本示例使用线程池实现两个线程交替打应数字,直到10
*/
private static Object obj1 = new Object();
private static int num = 0; public static void main(String[] args){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 10000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
//打印奇数线程
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println("奇数run");
while (true){
synchronized (obj1){
if(num > 10){
break;
}
System.out.println("奇数抢到" + num);
if(num/2 != 0){
System.out.println("奇:" + num);
num++;
obj1.notifyAll();
}else{
try {
System.out.println("奇数线程等待");
obj1.wait(); //wait会让出锁;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
});
//打印偶数线程
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println("偶数run");
while(true){
synchronized (obj1){
if(num > 10){
break;
}
System.out.println("偶数抢到" + num);
if(num/2 == 0){
System.out.println("偶:" + num);
num++;
obj1.notifyAll();
}else{
try {
System.out.println("偶数线程等待");
obj1.wait(); //wait会让出锁;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
});
}
}

意外发生了,我发现我随手写的代码是垃圾,下面是运行结果

分析一下结果

前面是上班期间匆匆忙忙写的代码,周末回家定睛一看,代码中一个很低级的错,就是取余%符号写成了除号/,我实在很是无语,突然让我想起今日听到的一句话——“粗心大意是基本功不扎实的表现”,本想把前面的都删了,转念一想,干嘛呢?写这些不就是记录自己的学习过程的吗,这些错误会成为以后回忆这篇文字的hook,都记下来吧。

改正后运行效果,程序实现了两个线程交替打印0和1

3.ThreadPoolExecutor类分析

3.1.构造方法
    前面例子中构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
显然最终构造方法是:
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //线程存活时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //任务队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) //拒绝策略
解释
1.核心线程数:当提交新任务时,线程池中线程小于核心线程数,则会创建一个线程处理该新任务。
2.最大线程数:当提交新任务时,如果任务队列已满且线程数量小于最大线程数时,则会创建一个新线程处理该任务。
3.线程存活时间:线程池中线程数量大于核心线程数时,空闲时间超过该设定时间的线程被销毁。
4.时间单位:存活时间的单位
5.任务队列:当线程数量等于核心线程设定值时,新提交的任务会被放到任务队列中。任务队列有好几种,后面再细说。
6.线程工厂:用来创建线程,可以控制线程所属组合线程名,后面细说。
7.拒绝策略:当提交新任务时,任务队列已满且线程达到最大线程数,此时调用拒绝策略,后面再细说。

ThreadPool总结参考

3.2.核心方法

3.3.线程池工作过程
<1>任务提交过程
submit()方法最终会调用execute()执行任务。execute()方法中多次调用addWorker方法,该方法的主要作用就是创建一个线程来执行Runnable对象。
execute()方法执行一个Runnable对象时,首先通过workerCountOf(c)获取线程池中线程的数量,如果池中的数量小于corePoolSize就调用addWorker添加一个线程来执行这个任务。否则通过workQueue.offer(command)方法入列。如果入列成功还需要在一次判断池中的线程数,因为我们创建线程池时可能要求核心线程数量为0,所以我们必须使用addWorker(null, false)来创建一个临时线程去阻塞队列中获取任务来执行。
提交时涉及构造方法中的四个参数corePoolSize、maxPoolSize、blockingQueue、rejectHandler。具体关系很好懂,结合前面参数理解,或者想象一下现实排队策略(正编员工,队列,临时工)就明白了。 <2>执行过程
Thread的run方法实际上调用了Worker类的runWorker方法。
Worker类是ThreadPoolExecutor类中私有类
private final class Worker
extends AbstractQueuedSynchronizer implements Runnable <3>关闭过程
涉及shutdown()、shutdownNow()、awaitTermination(long timeout, TimeUnit unit)方法
之前自己工作遇到的一个停止demo如下: threadPoolExecutor.shutdown(); //停止提交任务,执行完当前和队列中的任务(好比开始考试了,迟到的学生停止入场)
try {
if(!threadPoolExecutor.awaitTermination(120, TimeUnit.MINUTES)){ //120min过后检测是否停掉,中间即便早执行完,也不会返回(好比120分钟考试结束,判断是否所有学生都交卷)
System.out.println("规定时间内没有执行所有任务");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
threadPoolExecutor.shutdownNow(); //最后再强行停止(好比,有些学生最后被老师强行收卷,但老师可能还干不过学生)
}

线程池关闭&监控&结构图画法帮助的参考

3.4.类结构关系



Executor类并不是线程池,而只是一个执行线程的工具,真正的线程池接口是ExecutorService;

ScheduledExecutorService, 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题;

ThreadPoolExecutor,线程池的真正实现;

ScheduledThreadPoolExecutor,周期性任务调度的类实现;

4.Executors类和各种线程池

要配置一个线程池是比较复杂的,尤其是对线程池的原理不是很清楚的情况下,很可能配置的线程池不是较优的,因此Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1.newCachedThreadPool: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,新建线程(线程最大并发数不可控制)。
使用场景:CachedThreadPool 用于并发执行“大量短期的小任务”,或者是负载较轻的服务器。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} 2.newFixedThreadPool: 创建一个固定大小的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
使用场景:FixedThreadPool 用于负载比较重的服务器,为了资源的合理利用,需要限制当前线程数量。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
} 3.newScheduledThreadPool: 创建一个定时线程池,支持定时及周期性任务执行。
scheduleAtFixedRate,固定周期执行,周期到了没有分配到线程的任务就不执行了
scheduleWithFixedDelay,固定延迟,所有任务都会执行
execute(), 普通立马执行, 前面两种执行具体后面专门介绍一下,这里只是简单试验得出结论
使用场景:ScheduledThreadPoolExecutor 用于需要多个后台线程执行周期任务,同时需要限制线程数量的场景。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, //调用ThreadPoolExecutor的构造方法
new DelayedWorkQueue());
} 4.newSingleThreadPool: 创建一个单线程化得线程池,它只会用唯一的工作线程执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。
使用场景:SingleThreadExecutor 用于串行执行任务的场景,每个任务必须按顺序执行,不需要并发执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS, //keepAliveTime为0,表示空闲线程立即销毁,但此时corePoolSize=maxPoolSize,所以这个设置没有意义
new LinkedBlockingQueue<Runnable>())); //LinkedBlockingQueue队列最大容量Integer.MAX_VALUE,相当于没有上限
}

参考

5.其他

5.1.阿里手册对线程池使用约定
【强制】线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式创建,这样的处理方式能让编写代码的工程师更加明确线程池的运行规则,规避资源耗尽的风险。
附加说明:Executors返回线程池对象的弊端如下
1.FixedThreadPool和SingleThreadPool允许队列的长度Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2.CachedThreadPool和ScheduledThreadPool允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量线程,从而导致OOM。
5.2 其他一些忘记总结的知识点
1.线程池状态,通过ThreadPoolExecutor的成员变量控制
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING,0));
ctl主要用于存储线程池的工作状态以及池中正在运行的线程数。显然要在一个整型变量存储两个数据,只能将其一分为二。其中高3bit用于存储线程池的状态,低位的29bit用于存储正在运行的线程数。
五种状态:
RUNNING,允许提交并处理任务;
SHUTDOWN,不允许提交新任务,但会处理已提交任务;
STOP,不允许提交新任务,也不会处理阻塞队列中未执行的任务,并设置正在执行线程的中断标志位;
TIDYING,所有任务执行完毕,池中工作线程数量为0,等待执行terminated()钩子方法;
TERMINATED, terminated()钩子方法执行完毕;

java核心-多线程(6)-线程池-ThreadPoolExecutor的更多相关文章

  1. [转] 引用 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用

    PS: Spring ThreadPoolTaskExecutor vs Java Executorservice cachedthreadpool 引用 [轰隆隆] 的 Java自带的线程池Thre ...

  2. Java并发编程:线程池ThreadPoolExecutor

    多线程的程序的确能发挥多核处理器的性能.虽然与进程相比,线程轻量化了很多,但是其创建和关闭同样需要花费时间.而且线程多了以后,也会抢占内存资源.如果不对线程加以管理的话,是一个非常大的隐患.而线程池的 ...

  3. Java:多线程,线程池,ThreadPoolExecutor详解

    1. ThreadPoolExecutor的一个常用的构造方法 ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepA ...

  4. 【Java多线程】线程池-ThreadPoolExecutor

    ThreadPoolExecutor提供了四个构造方法: 我们以最后一个构造方法(参数最多的那个),对其参数进行解释: public ThreadPoolExecutor(int corePoolSi ...

  5. Java并发包中线程池ThreadPoolExecutor原理探究

    一.线程池简介 线程池的使用主要是解决两个问题:①当执行大量异步任务的时候线程池能够提供更好的性能,在不使用线程池时候,每当需要执行异步任务的时候直接new一个线程来运行的话,线程的创建和销毁都是需要 ...

  6. java核心-多线程(4)-线程类基础知识

    1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...

  7. Java:多线程,线程池,使用CompletionService通过Future来处理Callable的返回结果

    1. 背景 在Java5的多线程中,可以使用Callable接口来实现具有返回值的线程.使用线程池的submit方法提交Callable任务,利用submit方法返回的Future存根,调用此存根的g ...

  8. Java 基础 多线程和线程池基础

    一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...

  9. Java入门系列之线程池ThreadPoolExecutor原理分析思考(十五)

    前言 关于线程池原理分析请参看<http://objcoding.com/2019/04/25/threadpool-running/>,建议对原理不太了解的童鞋先看下此文然后再来看本文, ...

  10. Java:多线程,线程池,用Executors静态工厂生成常用线程池

    一: newSingleThreadExecutor 创建一个单线程的线程池,以无界队列方式运行.这个线程池只有一个线程在工作(如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它.)此线程池 ...

随机推荐

  1. Spring Boot 缓存应用 Ehcache 入门教程

    Ehcache 小巧轻便.具备持久化机制,不用担心JVM和服务器重启的数据丢失.经典案例就是著名的Hibernate的默认缓存策略就是用Ehcache,Liferay的缓存也是依赖Ehcache. 本 ...

  2. java中静态初始化块的执行顺序

    在java中,其应该是先于所有的方法执行. 下面是测试代码: public class Test1 { static{ System.out.println("执行静态初始化块test1.. ...

  3. I/O-<File实例>

    File n=new File("D:\2016.txt"); System.out.println("文件是否存在"+n.exists()); System. ...

  4. 【译】索引进阶(十七): SQL SERVER索引最佳实践

    [译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正] 原文链接:传送门. 在本章我们给出一些建议:贯穿本系列我们提取出了十四条基本指南,这些基本的指南将会帮助你为你的数据库创建最佳的索引 ...

  5. python列表操作方法详解

      列表 列表是Python中最基本的数据结构,列表是最常用的Python数据类型,列表是一个数据的集合,集合内可以放任何数据类型,可对集合方便的增删改查操作.Python已经内置确定序列的长度以及确 ...

  6. pandas中数据框DataFrame获取每一列最大值或最小值

    1.python中数据框求每列的最大值和最小值 df.min() df.max()

  7. unittest中的parameterized参数化

    一.安装插件 pip install parameterized 二.有默认参数情况与没有默认参数情况---1 注意:这种写法,只能给单个用例进行参数化,不能给多个用例使用,要每个用例都进行参数化. ...

  8. 十八 OGNL特殊符号的作用,#,%,$

    主要有哪些字符? #:获取Context的数据,构建map %: 强制解析OGNL,强制不解析OGNL $ : 在配置文件中(xml,属性文件(国际化))使用OGNL #的用法: <body&g ...

  9. KEIL的一些函数

    一 Predefined Functions:http://www.keil.com/support/man/docs/uv4cl/uv4cl_df_predeffunct.htm 主要有三角/反三角 ...

  10. 【PAT甲级】1020 Tree Traversals (25 分)(树知二求一)

    题意: 输入一个正整数N(N<=30),给出一棵二叉树的后序遍历和中序遍历,输出它的层次遍历. trick: 当30个点构成一条单链时,如代码开头处的数据,大约1e9左右的结点编号大小,故采用结 ...