自定义线程池

1. 线程池的作用

  1. 减少在创建和销毁线程上所花的时间以及系统资源的开销
  2. 如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存

2. 实现自定义的线程池

rt thread消息队列非阻塞例程 消息队列 线程池_线程池


构建步骤

  • 消息队列(任务队列)
  • 线程池
  • 测试
    想法:
    按照上面的构建步骤开始说:
  • 消息队列
    先有一个容器,有两个方法,一个 put 一个take ,这个队列要多个线程访问,所以用锁
  • 线程池
    有一个线程集合,有一个 消息队列,还有一个线程的size(创建几个线程)。还有一个执行的方法。
  • 线程池的线程
    先执行完自己的任务,在看线程池中的消息队列还有没有消息。如果有就拿一个消息来执行行。直到消息队列为空,将自己从线程集合中移除

3. 代码

1.创建消息队列
/**
 * 定义线程吃,首先是要定义阻塞队列
 */
@Slf4j
class BlockQueue<T> {
    /**
     * 存放消息的地方  双向链表, array比link性能好
     */
    private Deque<T> deque = new ArrayDeque<T>();

    /**
     * 定义锁
     */
    private ReentrantLock lock = new ReentrantLock();

    /**
     * 生产者 等待room
     */
    private Condition fullWaitSet = lock.newCondition();

    /**
     * 消费者等待room
     */
    private Condition emptyWaitSet = lock.newCondition();

    /**
     * 容量
     */
    private int capcity;


    public BlockQueue(int capcity) {
        this.capcity = capcity;
    }

    /**
     * 不带 等待时间的 take方法
     * @return T
     */
    public T take() {
        /*
            流程:
            - 上锁,
            - 看队列有没有
            - 有返回 ,没有等待
            - 唤醒
            - 解锁
         */
        lock.lock();
        try {
            log.debug("尝试take");
            while (true == deque.isEmpty()) {

                try {
                    log.debug("emptyWaitSet await");
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            log.debug("emptyWaitSet removefirst");
            T result = deque.removeFirst();
            fullWaitSet.signalAll();
            return result;
        } finally {
            lock.unlock();
        }

    }

    /**
     * 带等待时间的 take方法
     * @param timeOut  等待时间
     * @param unit     等待时间单位
     * @return   队列中第一个消息
     */
    public T poll(long timeOut, TimeUnit unit) {
        /*
            流程:
            - 上锁,
            - 看队列有没有
            - 有返回 ,没有等待
            - 唤醒
            - 解锁
         */
        lock.lock();
        try {
            log.debug("尝试poll");
            //变为统一的时间
            long delayTime = unit.toNanos(timeOut); 
            while (true == deque.isEmpty()) {
                try {
                    log.debug("emptyWaitSet await");
                    if (delayTime < 0) {
                        return null;
                    }
                    // 防止虚假唤醒。awaitNanos的返回值是 总等待时间 - 被唤醒的经历的时间
                    delayTime = emptyWaitSet.awaitNanos(delayTime);  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("emptyWaitSet removefirst");
            T result = deque.removeFirst();
            fullWaitSet.signalAll();
            return result;
        } finally {
            lock.unlock();
        }

    }

    /**
     * 放消息
     * @return T
     */
    public void put(T task) {
        /*
        流程
        - 判断满不满
        - 不满就放,满就等待
        - 唤醒等待线程
         */

        lock.lock();
        try {
            while (capcity == deque.size()) {
                try {
                    log.debug("队列已经满,fullwaitset:{}", fullWaitSet);
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            deque.addLast(task);
            emptyWaitSet.signalAll();

        } finally {
            lock.unlock();
        }
    }

}
2.线程池
/**
 * 自定义线程池  首先要有blockQueue(任务队列),线程池线程数,线程集合,
 * 线程池的作用就是创建线程并且执行任务
 */
@Slf4j
class ThreadPool {
    /**
     * 消息队列(任务队列)
     */
    private BlockQueue<Runnable> taskQueue;
    /**
     * 工作线程数
     */
    private int coreSize;
    /**
     * 请注意:这里的线程集合不是线程安全的,在消息队列为空的情况下,多个线程要从这个集合里面移除他。
     */
    private HashSet<Work> works = new HashSet<>();
    /**
     * 等待时间
     */
    private long awaitTime;
    /**
     * 等待时间单位
     */
    private TimeUnit unit;

    /**
     * 初始化
     *
     * @param coreSize      工作线程数
     * @param taskQueueSize 消息队列大小
     * @param awaitTime     消费者等待时间
     * @param unit          等待时间单位
     */
    public ThreadPool(int coreSize, int taskQueueSize, long awaitTime, TimeUnit unit) {
        this.coreSize = coreSize;
        this.taskQueue = new BlockQueue<Runnable>(taskQueueSize);
        this.awaitTime = awaitTime;
        this.unit = unit;
    }

    /**
     * 线程池 执行任务的方法
     *
     * @param task 需要执行的任务
     */
    public void execute(Runnable task) {
        /**
         * 创建线程执行任务.
         * 懒惰初始化;
         *   - 如果workset的数量小于coresize 那就创建,执行任务
         *   - 否则:任务进任务队列
         *  这里要加锁:因为work是公用的,
         */
        synchronized (works) {
            if (works.size() < coreSize) {
                //创建
                Work work = new Work(task);
                // 添加线程进works集合
                works.add(work);
                //开启线程
                work.start();
            } else {
                //任务进消息队列
                taskQueue.put(task);
            }
        }
    }

    /**
     * 内部类  工作线程
     */
    class Work extends Thread {
        /**
         * 任务
         */
        private Runnable task;

        public Work(Runnable task) {
            this.task = task;
        }

        /**
         * 重写run方法
         */
        @Override
        public void run() {
            /*
            这里的逻辑:
                - 执行自己的task 执行完之后 看队列中还有没有任务,如果有继续执行
             */

            //判断task是否为null,线程刚创建好的时候。task不是null,
            while (task != null) {
                try {
                    log.debug("线程正在运行");
                    task.run();
                } finally {
                    task = null;
                    // 从消息队列中取一个,赋值为task。有最大的等待时间
                    task = taskQueue.poll(awaitTime, unit);  		//如果用take方法。在所有的任务执行完了之后,线程不会退出,一直在等待。。。 
                    //修改方式:
                    	// 加一个超时等待。
                }
            }
            // 如果消息队列中没有消息,
            synchronized (works) {
                //从工作线程中移除 自己
                works.remove(this);
                log.debug("worker 被移除{}", this);
            }
        }
    }
}
3.测试
public static void main(String[] args) {
        ThreadPool threadPool = new ThreadPool(2, 1, 1, TimeUnit.SECONDS);
        threadPool.execute(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                log.debug("1");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadPool.execute(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                log.debug("2");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        threadPool.execute(() -> {
            log.debug("3");
        });
    }

结果:

rt thread消息队列非阻塞例程 消息队列 线程池_并发编程_02


这个功能没有完全: 后面会有增强版