本文分享自华为云社区《【高并发】扩大招了,冰河带你10分钟手撸Java线程池,yyds,从速保藏吧》,作者:冰 河。
Java线程池中心原理
看过Java线程池源码的小伙伴都知道,在Java线程池中最中心的类便是ThreadPoolExecutor,而在ThreadPoolExecutor类中最中心的结构办法便是带有7个参数的结构办法,如下所示。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
各参数的含义如下所示。
- corePoolSize:线程池中的常驻中心线程数。
- maximumPoolSize:线程池可以包容一起履行的最大线程数,此值大于等于1。
- keepAliveTime:剩余的闲暇线程存活时刻,当空间时刻达到keepAliveTime值时,剩余的线程会被毁掉直到只剩下corePoolSize个线程为止。
- unit:keepAliveTime的单位。
- workQueue:使命行列,被提交但没有被履行的使命。
- threadFactory:表明生成线程池中作业线程的线程工厂,用户创立新线程,一般用默许即可。
- handler:回绝策略,表明当线程行列满了而且作业线程大于等于线程池的最大显现数(maxnumPoolSize)时,如何来回绝请求履行的runnable的策略。
而且Java的线程池是经过 生产者-顾客形式 完成的,线程池的运用方是生产者,而线程池自身便是顾客。
Java线程池的中心作业流程如下图所示。
手撸Java线程池
咱们自己手动完成的线程池要比Java自身的线程池简略的多,咱们去掉了各种杂乱的处理方式,只保留了最中心的原理:线程池的运用者向使命行列中添加使命,而线程池自身从使命行列中消费使命并履行使命。
只需理解了这个中心原理,接下来的代码就简略多了。在完成这个简略的线程池时,咱们可以将整个完成进程进行拆解。拆解后的完成流程为:界说中心字段、创立内部类WorkThread、创立ThreadPool类的结构办法和创立履行使命的办法。
界说中心字段
首先,咱们创立一个名称为ThreadPool的Java类,并在这个类中界说如下中心字段。
- DEFAULT_WORKQUEUE_SIZE:静态常量,表明默许的堵塞行列巨细。
- workQueue:模仿实践的线程池运用堵塞行列来完成生产者-顾客形式。
- workThreads:模仿实践的线程池运用List调集保存线程池内部的作业线程。
中心代码如下所示。
//默许堵塞行列巨细
private static final int DEFAULT_WORKQUEUE_SIZE = 5;
//模仿实践的线程池运用堵塞行列来完成生产者-顾客形式
private BlockingQueue<Runnable> workQueue;
//模仿实践的线程池运用List调集保存线程池内部的作业线程
private List<WorkThread> workThreads = new ArrayList<WorkThread>();
创立内部类WordThread
在ThreadPool类中创立一个内部类WorkThread,模仿线程池中的作业线程。主要的效果便是消费workQueue中的使命,并履行使命。因为作业线程需求不断从workQueue中获取使命,所以,这里运用了while(true)循环不断尝试消费行列中的使命。
中心代码如下所示。
//内部类WorkThread,模仿线程池中的作业线程
//主要的效果便是消费workQueue中的使命,并履行
//因为作业线程需求不断从workQueue中获取使命,运用了while(true)循环不断尝试消费行列中的使命
class WorkThread extends Thread{
@Override
public void run() {
//不断循环获取行列中的使命
while (true){
//当没有使命时,会堵塞
try {
Runnable workTask = workQueue.take();
workTask.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
创立ThreadPool类的结构办法
这里,咱们为ThreadPool类创立两个结构办法,一个结构办法中传入线程池的容量巨细和堵塞行列,另一个结构办法中只传入线程池的容量巨细。
中心代码如下所示。
//在ThreadPool的结构办法中传入线程池的巨细和堵塞行列
public ThreadPool(int poolSize, BlockingQueue<Runnable> workQueue){
this.workQueue = workQueue;
//创立poolSize个作业线程并将其加入到workThreads调集中
IntStream.range(0, poolSize).forEach((i) -> {
WorkThread workThread = new WorkThread();
workThread.start();
workThreads.add(workThread);
});
}
//在ThreadPool的结构办法中传入线程池的巨细
public ThreadPool(int poolSize){
this(poolSize, new LinkedBlockingQueue<>(DEFAULT_WORKQUEUE_SIZE));
}
创立履行使命的办法
在ThreadPool类中创立履行使命的办法execute(),execute()办法的完成比较简略,便是将办法接收到的Runnable使命加入到workQueue行列中。
中心代码如下所示。
//经过线程池履行使命
public void execute(Runnable task){
try {
workQueue.put(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
完整源码
这里,咱们给出手动完成的ThreadPool线程池的完整源代码,如下所示。
package io.binghe.thread.pool;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.IntStream;
/**
* @author binghe
* @version 1.0.0
* @description 自界说线程池
*/
public class ThreadPool {
//默许堵塞行列巨细
private static final int DEFAULT_WORKQUEUE_SIZE = 5;
//模仿实践的线程池运用堵塞行列来完成生产者-顾客形式
private BlockingQueue<Runnable> workQueue;
//模仿实践的线程池运用List调集保存线程池内部的作业线程
private List<WorkThread> workThreads = new ArrayList<WorkThread>();
//在ThreadPool的结构办法中传入线程池的巨细和堵塞行列
public ThreadPool(int poolSize, BlockingQueue<Runnable> workQueue){
this.workQueue = workQueue;
//创立poolSize个作业线程并将其加入到workThreads调集中
IntStream.range(0, poolSize).forEach((i) -> {
WorkThread workThread = new WorkThread();
workThread.start();
workThreads.add(workThread);
});
}
//在ThreadPool的结构办法中传入线程池的巨细
public ThreadPool(int poolSize){
this(poolSize, new LinkedBlockingQueue<>(DEFAULT_WORKQUEUE_SIZE));
}
//经过线程池履行使命
public void execute(Runnable task){
try {
workQueue.put(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//内部类WorkThread,模仿线程池中的作业线程
//主要的效果便是消费workQueue中的使命,并履行
//因为作业线程需求不断从workQueue中获取使命,运用了while(true)循环不断尝试消费行列中的使命
class WorkThread extends Thread{
@Override
public void run() {
//不断循环获取行列中的使命
while (true){
//当没有使命时,会堵塞
try {
Runnable workTask = workQueue.take();
workTask.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
没错,咱们仅仅用了几十行Java代码就完成了一个极简版的Java线程池,没错,这个极简版的Java线程池的代码却体现了Java线程池的中心原理。
接下来,咱们测验下这个极简版的Java线程池。
编写测验程序
测验程序也比较简略,便是经过在main()办法中调用ThreadPool类的结构办法,传入线程池的巨细,创立一个ThreadPool类的实例,然后循环10次调用ThreadPool类的execute()办法,向线程池中提交的使命为:打印当前线程的名称--->> Hello ThreadPool
。
全体测验代码如下所示。
package io.binghe.thread.pool.test;
import io.binghe.thread.pool.ThreadPool;
import java.util.stream.IntStream;
/**
* @author binghe
* @version 1.0.0
* @description 测验自界说线程池
*/
public class ThreadPoolTest {
public static void main(String[] args){
ThreadPool threadPool = new ThreadPool(10);
IntStream.range(0, 10).forEach((i) -> {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "--->> Hello ThreadPool");
});
});
}
}
接下来,运行ThreadPoolTest类的main()办法,会输出如下信息。
Thread-0--->> Hello ThreadPool
Thread-9--->> Hello ThreadPool
Thread-5--->> Hello ThreadPool
Thread-8--->> Hello ThreadPool
Thread-4--->> Hello ThreadPool
Thread-1--->> Hello ThreadPool
Thread-2--->> Hello ThreadPool
Thread-5--->> Hello ThreadPool
Thread-9--->> Hello ThreadPool
Thread-0--->> Hello ThreadPool
至此,咱们自界说的Java线程池就开发完成了。
总结
线程池的中心原理其实并不杂乱,只需咱们耐性的分析,深化其源码理解线程池的中心实质,你就会发现线程池的设计原来是如此的优雅。希望经过这个手写线程池的小例子,可以让你更好的理解线程池的中心原理。
点击关注,第一时刻了解华为云新鲜技术~