本文分享自华为云社区《【高并发】扩大招了,冰河带你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线程池的中心作业流程如下图所示。

10分钟带你徒手写个Java线程池

手撸Java线程池

咱们自己手动完成的线程池要比Java自身的线程池简略的多,咱们去掉了各种杂乱的处理方式,只保留了最中心的原理:线程池的运用者向使命行列中添加使命,而线程池自身从使命行列中消费使命并履行使命。

10分钟带你徒手写个Java线程池
10分钟带你徒手写个Java线程池

只需理解了这个中心原理,接下来的代码就简略多了。在完成这个简略的线程池时,咱们可以将整个完成进程进行拆解。拆解后的完成流程为:界说中心字段、创立内部类WorkThread、创立ThreadPool类的结构办法和创立履行使命的办法。

10分钟带你徒手写个Java线程池
10分钟带你徒手写个Java线程池

界说中心字段

首先,咱们创立一个名称为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线程池就开发完成了。

总结

线程池的中心原理其实并不杂乱,只需咱们耐性的分析,深化其源码理解线程池的中心实质,你就会发现线程池的设计原来是如此的优雅。希望经过这个手写线程池的小例子,可以让你更好的理解线程池的中心原理。

点击关注,第一时刻了解华为云新鲜技术~