java 线程池由哪些组件组成_关于线程池,那些你还不知道的事

java 线程池由哪些组件组成_关于线程池,那些你还不知道的事

一、背景

最近在学习线程相关的知识,然后顺理成章少不了学习线程池,刚开始在没有深入的学习之前,感觉线程池是很神秘的东西,而且完全想不到怎么才能实现一个自己的线程池,然后还能保证它的可用性,然后就一直琢磨,琢磨了一周才不多,也是网上各种查资料,终于明白了线程池的原理,也自己手写一个线程池,来加深印象,那么本文我们就来聊一聊关于线程池的知识,希望更多的猿友能看到,从此对线程池有一个清晰直观的认识。

二、概念解析

1.什么是线程池

线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

2.使用线程池的好处

合理的使用线程池可以重复利用已创建的线程,这样就可以减少在创建线程和销毁线程上花费的时间和资源。并且,线程池在某些情况下还能动态的调整工作线程的数量,以平衡资源消耗和工作效率。同时线程池还提供了对池中工作线程进行统一的管理的相关方法。这样就相当于我们一次创建,就可以多次使用,大量的节省了系统频繁的创建和销毁线程所需要的资源。

3.线程池的主要组件

一个线程池包括以下四个基本组成部分:

(1)、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;

(2)、工作线程(WorkThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;

(3)、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;

(4)、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

4.JDK中线程池常用类UML类关系图

三、手写实现

我们知道了线程池的原理以及主要组件之后,就让我们来手动实现一个自己的线程池,以加深理解和深入学习。

1.线程池接口类

packagecom.hafiz.proxy.threadPool;importjava.util.List;/*** Desc:线程池接口类

* Created by hafiz.zhang on 2017/9/19.*/

public interfaceThreadPool {//执行单个线程任务

voidexecute(Runnable task);//执行多个任务

voidexecute(Runnable[] tasks);//执行多个任务

void execute(Listtasks);//返回已经执行的任务个数

intgetExecuteTaskNumber();//返回等待被处理的任务个数,队列的长度

intgetWaitTaskNumber();//返回正在工作的线程的个数

intgetWorkThreadNumber();//关闭线程池

voiddestroy();

}

2.线程池实现类ThreadPoolManager.java

packagecom.hafiz.proxy.threadPool;importjava.util.Arrays;importjava.util.List;importjava.util.Queue;importjava.util.concurrent.ConcurrentLinkedQueue;importjava.util.concurrent.atomic.AtomicLong;/*** Desc:线程池实现类

* Created by hafiz.zhang on 2017/9/19.*/

public class ThreadPoolManager implementsThreadPool {//线程池中默认线程的个数为5

private static Integer workerNum = 5;//工作线程数组

WorkThread[] workThreads;//正在执行的线程任务数量

private static volatile Integer executeTaskNumber = 0;//任务队列, 作为一个缓冲

private Queue taskQueue = new ConcurrentLinkedQueue<>();//单例模式

private staticThreadPoolManager threadPool;private AtomicLong threadNum = newAtomicLong();privateThreadPoolManager() {this(ThreadPoolManager.workerNum);

}private ThreadPoolManager(intworkerNum) {if (workerNum > 0) {

ThreadPoolManager.workerNum=workerNum;

}

workThreads= newWorkThread[ThreadPoolManager.workerNum];for (int i = 0; i < ThreadPoolManager.workerNum; i++) {

workThreads[i]= newWorkThread();

Thread thread= new Thread(workThreads[i], "ThreadPool-worker-" +threadNum.incrementAndGet());

thread.start();

System.out.println("初始化线程总数:" + (i+1) + ",当前线程名称是:ThreadPool-worker-" +threadNum);

}

}public staticThreadPool getThreadPool() {returngetThreadPool(workerNum);

}public static ThreadPool getThreadPool(intworkerNum) {if (workerNum > 0) {

ThreadPoolManager.workerNum=workerNum;

}if (threadPool == null) {

threadPool= newThreadPoolManager(ThreadPoolManager.workerNum);

}returnthreadPool;

}

@Overridepublic voidexecute(Runnable task) {synchronized(taskQueue) {

taskQueue.add(task);

taskQueue.notifyAll();

}

}

@Overridepublic voidexecute(Runnable[] tasks) {

execute(Arrays.asList(tasks));

}

@Overridepublic void execute(Listtasks) {synchronized(taskQueue) {for(Runnable task : tasks) {

taskQueue.add(task);

}

taskQueue.notifyAll();

}

}

@OverridepublicString toString() {return "ThreadPoolManager{" +

"当前的工作线程数量=" + getWorkThreadNumber() +

", 已完成的任务数=" + getExecuteTaskNumber() +

", 等待任务数=" + getWaitTaskNumber() +

'}';

}

@Overridepublic intgetExecuteTaskNumber() {returnexecuteTaskNumber;

}

@Overridepublic intgetWaitTaskNumber() {returntaskQueue.size();

}

@Overridepublic intgetWorkThreadNumber() {returnworkerNum;

}

@Overridepublic voiddestroy() {while (!taskQueue.isEmpty()) {try{

Thread.sleep(10);

}catch(InterruptedException e) {

e.printStackTrace();

}

}for (int i = 0; i < workThreads.length; i++) {

workThreads[i].shutdown();

workThreads[i]= null;

}

threadPool= null;

taskQueue.clear();

}private class WorkThread implementsRunnable {//线程是否可用

private boolean isRunning = true;

@Overridepublic voidrun() {

Runnable r= null;while(isRunning) {//队列同步机制,加锁

synchronized(taskQueue) {while (isRunning &&taskQueue.isEmpty()) {try{

taskQueue.wait(20);

}catch(InterruptedException e) {

e.printStackTrace();

}

}if (!taskQueue.isEmpty()) {

r=taskQueue.poll();

}

}if (r != null) {

r.run();

}

executeTaskNumber++;

r= null;

}

}public voidshutdown() {

isRunning= false;

}

}

}

其中该类中包含内部类WorkThread,它用来包装真正的线程类,给每一个线程一个是否可用的标志,该线程工作室同步的从taskQueue中取出要执行的任务进行调用run方法来执行任务。

这个类中的getThreadPool方法中我们还使用到了懒汉式来实现单例,单例模式也是Java常用设计模式之一。

注意该类中的destroy方法的实现:我们是一直等到队列中的所有的任务执行完毕,才真正的销毁线程池,销毁的过程中不要忘记将每一个线程对象置为null,并且清空任务队列,这样更利于java的垃圾回收。

3.自定义任务类Task.java

packagecom.hafiz.proxy.threadPool;/*** Desc:自定义任务类

* Created by hafiz.zhang on 2017/9/21.*/

public class Task implementsRunnable {private static volatile Integer i = 1;

@Overridepublic voidrun() {//执行任务

synchronized(i) {

System.out.println("当前处理的线程是:" + Thread.currentThread().getName() + ",执行任务:" + (i++) + "完成");

}

}

}

4.线程池测试类

packagecom.hafiz.proxy.threadPool;importjava.util.ArrayList;importjava.util.List;/*** Desc:线程池测试类

* Created by hafiz.zhang on 2017/9/20.*/

public classThreadPoolTest {public static voidmain(String[] args) {

ThreadPool t= ThreadPoolManager.getThreadPool(6);

List tasks = new ArrayList<>();for (int i = 0; i < 100; i++) {

tasks.add(newTask());

}

System.out.println(t);

t.execute(tasks);//所有的线程执行完成才destroy

t.destroy();

System.out.println(t);

}

}

5.测试结果:(为了篇幅,只创建10个任务运行)

四、总结

通过本文,我们弄明白线程池到底是怎么工作,学习知识的过程中,我们就是要知其然知其所以然。这样我们才能更好地驾驭它,才能更好地去理解和使用,也能更好地帮助我们触类旁通,后面有机会我们接着来说数据库连接池的原理及手写实现。

相关文章