java实现手写一个简单版的线程池

网友投稿 311 2022-12-22

java实现手写一个简单版的线程池

有些人可能对线程池比较陌生,并且更不熟悉线程池的工作原理。所以他们在使用线程的时候,多数情况下都是new Thread来实现多线程。但是,往往良好的多线程设计大多都是使用线程池来实现的。 为什么要使用线程 降低资源的消耗。降低线程创建和销毁的资源消耗。提高响应速度:线程的创建时间为T1,执行时间T2,销毁时间T3,免去T1和T3的时间提高线程的可管理性

下图所示为线程池的实现原理:调用方不断向线程池中提交任务;线程池中有一组线程,不断地从队列中取任务,这是一个典型的生产者-消费者模型。

要实现一个线程池,有几个问题需要考虑:

队列设置多长?如果是无界的,调用方不断往队列中方任务,可能导致内存耗尽。如果是有界的,当队列满了之后,调用方如何处理?

线程池中的线程个数是固定的,还是动态变化的?

每次提交新任务,是放入队列?还是开新线程

当没有任务的时候,线程是睡眠一小段时间?还是进入阻塞?如果进入阻塞,如何唤醒?

针对问题4,有3种做法:

不使用阻塞队列,只使用一般的线程安全的队列,也无阻塞/唤醒机制。当队列为空时,线程池中的线程只能睡眠一会儿,然后醒来去看队列中有没有新任务到来,如此不断轮询。

不使用阻塞队列,但在队列外部,线程池内部实现了阻塞/唤醒机制

使用阻塞队列

很显然,做法3最完善,既避免了线程池内部自己实现阻塞/唤醒机制的麻烦,也避免了做法1的睡眠/轮询带来的资源消耗和延迟。

现在来带大家手写一个简单的线程池,让大家更加理解线程池的工作原理

实战:手写简易线程池

根据上图可以知道,实现线程池需要一个阻塞队列+存放线程的容器

/**

* Five在努力

* 自定义线程池

*/

public class ThreadPool {

/** 默认线程池中的线程的数量 */

private static final int WORK_NUM = 5;

/** 默认处理任务的数量 */

private static final int TASK_NUM = 100;

/** 存放任务 */

private final BlockingQueue ONNYbwpSItaskQueue;

private final Set workThreads;//保存线程的集合

private int workNumber;//线程数量

private int taskNumber;//任务数量

public ThreadPool(){

this(WORK_NUM , TASK_NUM);

}

public ThreadPool(int workNumber , int taskNumber) {

if (taskNumber<=0){

taskNumber = TASK_NUM;

}

if (workNumber<=0){

workNumber = WORK_NUM;

}

this.taskQueue = new ArrayBlockingQueue(taskNumber);

this.workNumber = workNumber;

this.taskNumber = taskNumber;

workThreads = new HashSet<>();

//工作线程准备好了

//启动一定数量的线程数,从队列中获取任务处理

for (int i=0;i

WorkThread workThread = new WorkThread("thead_"+i);

workThread.start();

workThreads.add(workThread);

}

}

/**

* 线程池执行任务的方法,其实就是往BlockingQueue中添加元素

* @param task

*/

public void execute(Runnable task) {

try {

taskQueue.put(task);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 销毁线程池

*/

public void destroy(){

System.out.println("ready close pool...");

for (WorkThread workThread : workThreads) {

workThread.stopWorker();

workThread = null;//help gc

}

workThreads.clear();

}

/** 内部类,工作线程的实现 */

private class WorkThread extends Thread{

public WorkThread(String name){

super();

setName(name);

}

@Override

public void run() {

while (!interrupted()) {

try {

Runnable runnable = taskQueue.take();//获取任务

if (runnable !=null) {

System.out.println(getName()+" ready execute:"+runnable.toString());

runnable.run();//执行任务

}

runnable = null;//help gc

} catch (Exception e) {

interrupt();

e.printStackTrace();

}

}

}

public void stopWorker(){

interrupt();

}

}

}

上面代码定义了默认的线程数量和默认处理任务数量,同时用户也可以自定义线程数量和处理任务数量。用BlockingQueue阻塞队列来存放任务。用set来存放工作线程,set的好处就不用多说了。懂的都懂

构造方法中new对象的时候,循环启动线程,并把线程放入set中。WorkThread实现Thread,run方法实现也很简单,因为有一个stop方法,所以这里需要while判断,之后从taskQueue队列中,获取任务。如何获取不到就阻塞,获取到的话runnable.run();就执行任务,之后把任务变成null

销毁线程只需要遍历set,把每个线程停止,并且变为null就行了

执行线程任务execute,只需要从往阻塞队列中添加任务就行了

测试一下:

public class TestMySelfThreadPool {

private static final int TASK_NUM = 50;//任务的个数

public static void main(String[] args) {

ThreadPool myPool = new ThreadPool(3,50);

for (int i=0;i

myPool.execute(new MyTask("task_"+i));

}

}

static class MyTask implements Runnable{

private String name;

public MyTask(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("task :"+name+" end...");

}

@Override

public String toString() {

// TODO Auto-generated method stub

return "name = "+name;

}

}

}

结果ok。没什么问题

WorkThread workThread = new WorkThread("thead_"+i);

workThread.start();

workThreads.add(workThread);

}

}

/**

* 线程池执行任务的方法,其实就是往BlockingQueue中添加元素

* @param task

*/

public void execute(Runnable task) {

try {

taskQueue.put(task);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 销毁线程池

*/

public void destroy(){

System.out.println("ready close pool...");

for (WorkThread workThread : workThreads) {

workThread.stopWorker();

workThread = null;//help gc

}

workThreads.clear();

}

/** 内部类,工作线程的实现 */

private class WorkThread extends Thread{

public WorkThread(String name){

super();

setName(name);

}

@Override

public void run() {

while (!interrupted()) {

try {

Runnable runnable = taskQueue.take();//获取任务

if (runnable !=null) {

System.out.println(getName()+" ready execute:"+runnable.toString());

runnable.run();//执行任务

}

runnable = null;//help gc

} catch (Exception e) {

interrupt();

e.printStackTrace();

}

}

}

public void stopWorker(){

interrupt();

}

}

}

上面代码定义了默认的线程数量和默认处理任务数量,同时用户也可以自定义线程数量和处理任务数量。用BlockingQueue阻塞队列来存放任务。用set来存放工作线程,set的好处就不用多说了。懂的都懂

构造方法中new对象的时候,循环启动线程,并把线程放入set中。WorkThread实现Thread,run方法实现也很简单,因为有一个stop方法,所以这里需要while判断,之后从taskQueue队列中,获取任务。如何获取不到就阻塞,获取到的话runnable.run();就执行任务,之后把任务变成null

销毁线程只需要遍历set,把每个线程停止,并且变为null就行了

执行线程任务execute,只需要从往阻塞队列中添加任务就行了

测试一下:

public class TestMySelfThreadPool {

private static final int TASK_NUM = 50;//任务的个数

public static void main(String[] args) {

ThreadPool myPool = new ThreadPool(3,50);

for (int i=0;i

myPool.execute(new MyTask("task_"+i));

}

}

static class MyTask implements Runnable{

private String name;

public MyTask(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("task :"+name+" end...");

}

@Override

public String toString() {

// TODO Auto-generated method stub

return "name = "+name;

}

}

}

结果ok。没什么问题

myPool.execute(new MyTask("task_"+i));

}

}

static class MyTask implements Runnable{

private String name;

public MyTask(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println("task :"+name+" end...");

}

@Override

public String toString() {

// TODO Auto-generated method stub

return "name = "+name;

}

}

}

结果ok。没什么问题

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java使用pulsar
下一篇:一篇文章教你如何用多种迭代写法实现二叉树遍历
相关文章

 发表评论

暂时没有评论,来抢沙发吧~