自己手写一个简易版本的线程池【Java】【详细注解,含思考过程和知识点】

@Slf4j
public class Test15 {

    public static void main(String[] args) {
        ThreadPool pool = new ThreadPool(1, 1000, TimeUnit.MILLISECONDS, 1, (taskQueue, task) -> {
            // 这里实现自己的拒绝策略

            // 死等
//            taskQueue.put(task);

            // 带超时时间的阻塞添加
            taskQueue.offer(task, 1000, TimeUnit.MILLISECONDS);
        });

        for (int i = 0; i < 3; i++) {
            int j = i;
            pool.execute(() -> {
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("执行任务:{}", j);
            });
        }

    }
}

@Slf4j
// 自己手写一个简易的线程池
class ThreadPool {
    /**
     * 什么是线程池呢?
     * 顾名思义,线程池就是一个池子里面,有许多的线程。
     * 线程池是与工作队列密切相关的,其中在工作对垒中保存了所有等待执行的任务。
     * 线程池里的工作线程(Worker)的任务很简单:
     * 就是从工作队列中获取一个任务,然后执行,执行完之后,返回线程池,等待下一个任务。
     * <p>
     * 线程池有啥好处呢?
     * 可以重复使用线程池中的线程。避免大量的线程创建和销毁(开销巨大的操作)。从而提升效率。
     * 通过适当调整线程池的大小,可以充分的利用 cpu ,让 cpu 一直处于忙碌的状态,不让他闲下来。
     * 同时可以避免过多的线程,相互竞争,导致大量的上下文切换,影响效率。
     */

    // 存放任务的阻塞队列
    private BlockingQueue<Runnable> taskQueue;

    // 工作线程集合
    private Set<Worker> workers = new HashSet<>();

    // 核心线程数量
    private int coreSize;

    // 获取任务的超时时间
    private long timeout;

    private TimeUnit timeUnit;

    // 拒绝策略
    private RejectPolicy<Runnable> rejectPolicy;


    public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int capacity, RejectPolicy<Runnable> rejectPolicy) {
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        taskQueue = new BlockingQueue<>(capacity);
        this.rejectPolicy = rejectPolicy;
    }

    // 让线程池来执行任务
    public void execute(Runnable task) {
        synchronized (workers) {
            // 如果工作线程的数量小于 coreSize,则直接新建线程来执行任务
            if (workers.size() < coreSize) {
                Worker worker = new Worker(task);
                log.debug("新增了 worker 线程{}", worker);
                workers.add(worker);
                worker.start();
            } else {
                // 如果工作线程的数量 >= coreSize,则将任务加入到任务队列中,让空闲的工作线程去获取执行
//                taskQueue.put(task);
                // 拒绝策略:【把具体的实现,交给调用者来自己写】
                // 1.死等
                // 2.带超时时间的等待
                // 3.让调用者放弃任务的执行
                // 4.让调用者抛出异常
                // 5.让调用者自己执行

                taskQueue.tryPut(rejectPolicy, task);
            }
        }
    }

    class Worker extends Thread {

        private Runnable task;

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

        // 1.当task不为空时,直接执行任务
        // 2.当task为空时,去任务队列中去获取任务
        @Override
        public void run() {
//            while (task != null || (task = taskQueue.take()) != null) {
            while (task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
                try {
                    log.debug("执行任务 {} ", task);
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            // 代码运行到这里,说明任务队列中也没有任务了。移除掉这个 worker 线程
            synchronized (workers) {
                log.debug("移除 worker 工作线程 {}", this);
                workers.remove(this);
            }
        }
    }
}

@FunctionalInterface
interface RejectPolicy<T> {
    void reject(BlockingQueue<T> taskQueue, T task);
}

@Slf4j
class BlockingQueue<T> {

    // 任务队列
    private Deque<T> queue = new ArrayDeque<>();

    // 锁,防止多个线程同时获取头部的任务时,发生并发安全问题,导致一个任务被重复执行多次;
    // 防止多个线程同时向尾部加入新的任务时,发生并发安全问题
    private ReentrantLock lock = new ReentrantLock();

    // 生产者的条件变量,当队列满的时候,生产者线程就到 fullWaitSet 休息
    private Condition fullWaitSet = lock.newCondition();

    // 消费者的条件变量,当队列空的时候,消费者线程就到 emptyWaitSet 休息
    private Condition emptyWaitSet = lock.newCondition();

    // 队列容量
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    // 加入任务
    public void put(T task) {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                // 进入 fullWaitSet 休息室等待
                try {
                    log.debug("任务 {} 等待加入任务队列 ...", task);
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("任务 {} 加入任务队列", task);
            queue.addLast(task);
            // 记得唤醒 emptyWaitSet 休息室中等待的线程
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }

    // 带超时时间的阻塞添加
    public boolean offer(T task, long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == capacity) {
                // 进入 fullWaitSet 休息室等待
                try {
                    log.debug("任务 {} 等待加入任务队列 ...", task);
                    if (nanos <= 0) {
                        return false;
                    }
                    nanos = fullWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("任务 {} 加入任务队列", task);
            queue.addLast(task);
            // 记得唤醒 emptyWaitSet 休息室中等待的线程
            emptyWaitSet.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

    // 一直死等的阻塞获取
    public T take() {
        lock.lock();
        try {
            while (queue.size() == 0) {
                try {
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T task = queue.removeFirst();
            // 记得唤醒 fullWaitSet 休息室中等待的线程
            fullWaitSet.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }

    // 带超时时间的阻塞获取
    public T poll(long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == 0) {
                try {
                    if (nanos <= 0) {
                        return null;
                    }
                    // 返回值是:还要等待的时间。
                    // 将返回值赋值给 nanos,这样被虚假唤醒之后,还要重头开始等待那么多的时间。
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T task = queue.removeFirst();
            // 记得唤醒 fullWaitSet 休息室中等待的线程
            fullWaitSet.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }

    // 获取队列大小
    public int size() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }
    }

    public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
        lock.lock();
        try {
            if (queue.size() == capacity) {
                rejectPolicy.reject(this, task);
            } else {
                // 任务队列还没满
                log.debug("加入任务队列 {}", task);
                queue.add(task);
                emptyWaitSet.signal();;
            }
        } finally {
            lock.unlock();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一切随缘~~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值