Loading...
墨滴

阿啾

2021/07/12  阅读:62  主题:红绯

手写一个简单版的线程池

  有些人可能对线程池比较陌生,并且更不熟悉线程池的工作原理。所以他们在使用线程的时候,多数情况下都是new Thread来实现多线程。但是,往往良好的多线程设计大多都是使用线程池来实现的。

为什么要使用线程池

  1. 可以降低线程创建和销毁的资源消耗。
  2. 可以提高响应速度。线程的创建时间为T1,执行时间T2,销毁时间T3,通过使用线程池,可以免去T1和T3的时间
  3. 可以提高线程的可管理性

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

实战:手写简易线程池

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

/**
 * 自定义线程池
 */

public class ThreadPool {

    /** 默认线程池中的线程的数量 */
    private static final int WORK_NUM = 5;

    /** 默认处理任务的数量 */
    private static final int TASK_NUM = 100;

    /** 存放任务 */
    private final BlockingQueue<Runnable> taskQueue;

    private final Set<WorkThread> 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<Runnable>(taskNumber);
        this.workNumber = workNumber;
        this.taskNumber = taskNumber;

        workThreads = new HashSet<>();

        //工作线程准备好了
        //启动一定数量的线程数,从队列中获取任务处理
        for (int i=0;i<workNumber;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来存放工作线程;
   构造方法中new对象的时候,循环启动线程,并把线程放入set中。
   通过实现实现Thread来创建WorkThread类; 因为有一个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<TASK_NUM;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,没什么问题。

阿啾

2021/07/12  阅读:62  主题:红绯

作者介绍

阿啾