多线程系列六:线程池

一. 线程池简介

1. 线程池的概念:

线程池就是首先创建一些线程,它们的集合称为线程池。

2. 使用线程池的好处

a) 降低资源的消耗。使用线程池不用频繁的创建线程和销毁线程

b) 提高响应速度,任务:T1创建线程时间,T2任务执行时间,T3线程销毁时间,线程池空闲的时候可以去执行T1和T2,从而提高响应

c) 提高线程的可管理性。

使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

3. 线程池的工作机制

2.1 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。

2.1 一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

4. 使用线程池的原因:

多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。

5. 线程池的主要处理流程

说明:

a)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程b。

b)线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程c。

c)线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

6. ThreadPoolExecutor执行execute()方法的示意

执行execute()方法是对第5点中的线程池的主要处理流程的更深层次的说明

a)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

b)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。

c)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

d)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

7.线程池的创建各个参数含义

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

corePoolSize

线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;

如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;

如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。

maximumPoolSize

线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize

keepAliveTime

线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间。默认情况下,该参数只在线程数大于corePoolSize时才有用

TimeUnit

keepAliveTime的时间单位

workQueue

workQueue必须是BlockingQueue阻塞队列。当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待。通过workQueue,线程池实现了阻塞功能

threadFactory

创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名

Executors静态工厂里默认的threadFactory,线程的命名规则是“pool-数字-thread-数字”

8.RejectedExecutionHandler(饱和策略)

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

(1)AbortPolicy:直接抛出异常,默认策略;

(2)CallerRunsPolicy:用调用者所在的线程来执行任务;

(3)DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;

(4)DiscardPolicy:直接丢弃任务;

当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

9.关闭线程池

shutDown():interrupt方法来终止线程

shutDownNow() 尝试停止所有正在执行的线程

10. 合理地配置线程池

线程数配置:

任务:计算密集型,IO密集型,混合型

计算密集型适合配置的线程数=计算机的cpu数或计算机的cpu数+1(应付页缺失)

IO密集型适合配置的线程数=计算机的cpu数*2

混合型适合配置的线程数,拆分成计算密集型,IO密集型

Runtime.getRuntime().availableProcessors();当前机器中的cpu核心个数

队列的选择:

尽量有界队列,不要使用无界队列

二、使用jdk中线程池的案例

 1 import java.util.Random;
 2 import java.util.concurrent.ArrayBlockingQueue;
 3 import java.util.concurrent.ThreadPoolExecutor;
 4 import java.util.concurrent.TimeUnit;
 5
 6 /**
 7  * 使用jdk中线程池的案例
 8  */
 9 public class UseThreadPool {
10
11     static class MyTask implements Runnable {
12
13         private String name;
14
15
16         public MyTask(String name) {
17             this.name = name;
18         }
19
20         public String getName() {
21             return name;
22         }
23
24         @Override
25         public void run() {// 执行任务
26             try {
27                 Random r = new Random();
28                 Thread.sleep(r.nextInt(1000)+2000);
29             } catch (InterruptedException e) {
30                 System.out.println(Thread.currentThread().getId()+" sleep InterruptedException:"
31                         +Thread.currentThread().isInterrupted());
32             }
33             System.out.println("任务 " + name + " 完成");
34         }
35     }
36
37     public static void main(String[] args) {
38         //创建线程池
39         ThreadPoolExecutor threadPoolExecutor =
40                 new ThreadPoolExecutor(2,4,60,
41                 TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(10));
42         //往线程池里面提交6个线程去执行
43         for(int i =0;i<=5;i++){
44             MyTask task = new MyTask("Task_"+i);
45             System.out.println("A new task will add:"+task.getName());
46             threadPoolExecutor.execute(task);
47
48         }
49         //关闭线程池
50         threadPoolExecutor.shutdown();
51     }
52
53
54 }

三、实现自己的一个线程池

手写的线程池MyThreadPool

 1 import java.util.LinkedList;
 2 import java.util.List;
 3
 4 /**
 5  * 实现自己的一个线程池
 6  */
 7 public class MyThreadPool {
 8
 9     //默认的线程个数
10     private int work_num = 5;
11
12     //线程的容器
13     private WorkThread[] workThreads;
14
15     //任务队列
16     private List<Runnable> taskQueue = new LinkedList<>();
17
18     public MyThreadPool(int work_num) {
19         this.work_num = work_num;
20         workThreads = new WorkThread[work_num];
21         for(int i=0;i<work_num;i++){
22             workThreads[i] = new WorkThread();
23             workThreads[i].start();
24         }
25     }
26
27     //提交任务的接口
28     public void execute(Runnable task){
29         synchronized (taskQueue){
30             taskQueue.add(task);
31             taskQueue.notify();
32         }
33     }
34
35     //销毁线程池
36     public void destroy(){
37         System.out.println("ready stop pool....");
38         for(int i=0;i<work_num;i++){
39             workThreads[i].stopWorker();
40             workThreads[i] = null;//加速垃圾回收
41         }
42         taskQueue.clear();
43     }
44
45     //工作线程
46     private class WorkThread extends Thread{
47
48         private volatile boolean on = true;
49
50         public void run(){
51             Runnable r = null;
52             try{
53                 while(on&&!isInterrupted()){
54                     synchronized (taskQueue){
55                         //任务队列中无任务,工作线程等待
56                         while(on&&!isInterrupted()&&taskQueue.isEmpty()){
57                             taskQueue.wait(1000);
58                         }
59                         //任务队列中有任务,拿任务做事
60                         if(on&&!isInterrupted()&&!taskQueue.isEmpty()){
61                             r = taskQueue.remove(0);
62                         }
63                     }
64                     if (r!=null){
65                         System.out.println(getId()+" ready execute....");
66                         r.run();
67                     }
68                     //加速垃圾回收
69                     r = null;
70                 }
71
72             }catch(InterruptedException e){
73                 System.out.println(Thread.currentThread().getId()+" is Interrupted");
74             }
75         }
76
77         public void stopWorker(){
78             on = false;
79             interrupt();
80         }
81
82     }
83
84 }

测试手写实现的线程池TestMyThreadPool

 1 import java.util.Random;
 2
 3 /**
 4  * 测试手写实现的线程池
 5  */
 6 public class TestMyThreadPool {
 7     public static void main(String[] args) throws InterruptedException {
 8         // 创建3个线程的线程池
 9         MyThreadPool t = new MyThreadPool(3);
10         t.execute(new MyTask("testA"));
11         t.execute(new MyTask("testB"));
12         t.execute(new MyTask("testC"));
13         t.execute(new MyTask("testD"));
14         t.execute(new MyTask("testE"));
15         System.out.println(t);
16         Thread.sleep(3000);
17         t.destroy();// 所有线程都执行完成才destory
18         System.out.println(t);
19     }
20
21     // 任务类
22     static class MyTask implements Runnable {
23
24         private String name;
25         private Random r = new Random();
26
27         public MyTask(String name) {
28             this.name = name;
29         }
30
31         public String getName() {
32             return name;
33         }
34
35         @Override
36         public void run() {// 执行任务
37             try {
38                 Thread.sleep(r.nextInt(1000)+2000);
39             } catch (InterruptedException e) {
40                 System.out.println(Thread.currentThread().getId()+" sleep InterruptedException:"
41                         +Thread.currentThread().isInterrupted());
42             }
43             System.out.println("任务 " + name + " 完成");
44         }
45     }
46 }

原文地址:https://www.cnblogs.com/leeSmall/p/8443211.html

时间: 2024-08-28 01:06:26

多线程系列六:线程池的相关文章

多线程系列(2)线程池ThreadPool

上一篇文章我们总结了多线程最基础的知识点Thread,我们知道了如何开启一个新的异步线程去做一些事情.可是当我们要开启很多线程的时候,如果仍然使用Thread我们需要去管理每一个线程的启动,挂起和终止,显然是很麻烦的一件事情.还好.net framework为我们提供了线程池ThreadPool来帮助我们来管理这些线程,这样我们就不再需要手动地去终止这些线程.这一篇文章就让我们来学习一下线程池ThreadPool吧.关于它我想从以下几个方面进行总结. 认识线程池ThreadPool Thread

Java多线程系列 JUC线程池07 线程池原理解析(六)

 关闭“线程池” shutdown()的源码如下: public void shutdown() { final ReentrantLock mainLock = this.mainLock; // 获取锁 mainLock.lock(); try { // 检查终止线程池的“线程”是否有权限. checkShutdownAccess(); // 设置线程池的状态为关闭状态. advanceRunState(SHUTDOWN); // 中断线程池中空闲的线程. interruptIdleWork

Java多线程系列--“JUC线程池”01之 线程池架构

概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容——线程池.内容包括:线程池架构图线程池示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509903.html 线程池架构图 线程池的架构图如下: 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行

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

线程池示例 在分析线程池之前,先看一个简单的线程池示例. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class ThreadPoolDemo1 { public static void main(String[] args) { // 创建一个可重用固定线程数的线程池 ExecutorService pool = Executors.newFixedThre

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

拒绝策略介绍 线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施.当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭.第二,任务数量超过线程池的最大限制. 线程池共包括4种拒绝策略,它们分别是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy和DiscardPolicy. AbortPolicy -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常. Calle

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

概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3544116.html Callable 和 Future 简介 Callable 和 Future 是比较有趣的一对组合.当我们需要获取线程的执行结果时,就需要用到它们.Callable用于产生结果,Future用于获取结果. 1. Callable Calla

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

本章介绍线程池的生命周期. 线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态:然而,线程池不同于线程,线程池的5种状态是:Running, SHUTDOWN, STOP, TIDYING, TERMINATED. 线程池状态定义代码如下: /** * The main pool control state, ctl, is an atomic integer packing * two conceptual fields * workerCount, indi

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

ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存放一定数量线程的一个线程集合.线程池允许同时运行的线程数量就是线程池的容量:当添加到线程池中的线程超过它的容量时,会有一部分线程阻塞等待.线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理." ThreadPoolExecutor数据结构 ThreadPoolExecutor的数据结构如下图所示: 各个数据在ThreadPoolExecutor

Android多线程编程之线程池学习篇(一)

Android多线程编程之线程池学习篇(一) 一.前言 Android应用开发中多线程编程应用比较广泛,而应用比较多的是ThreadPoolExecutor,AsyncTask,IntentService,HandlerThread,AsyncTaskLoader等,为了更详细的分析每一种实现方式,将单独成篇分析.后续篇章中可能涉及到线程池的知识,特此本篇分析为何使用线程池,如何使用线程池以及线程池的使用原理. 二.Thread Pool基础 进程代表一个运行中的程序,一个运行中的Android

Java并发基础(六) - 线程池

Java并发基础(六) - 线程池 1. 概述 这里讲一下Java并发编程的线程池的原理及其实现 2. 线程池的基本用法 2.1 线程池的处理流程图 该图来自<Java并发编程的艺术>: 从图中我们可以看出当一个新任务到线程池时,线程池的处理流程如下: 线程池首先判断线程池里面线程数是否达到核心线程数.如果不是则直接创建新线程作为核心线程来执行该任务(该线程作为核心线程不会由于任务的完成而销毁),否则进入下一流程. 判断阻塞队列是否已经满了.如果没满则将该任务放入阻塞队列中,等待核心线程处理,