手写线程池

1.定义线程池接口

/**
 * 线程池接口,用于对线程进行管理
 * 1.执行一个任务
 * 2.关闭线程池
 * 3.增加线程池中线程的数量
 * 4.减少线程池中线程的数量
 **/
public interface ThreadPool<Job extends Callable> {

    /**
     * 将任务放入到线程池中进行执行
     * @param job 继承了callable的任务
     * @return
     */
    boolean execute(Job job);

    /**
     * 关闭线程池
     * @return
     */
    boolean shutdown();

    /**
     * 增加线程池中工作的线程的数量
     * @param num 需要增加的数量
     * @return
     */
    boolean addWorkers(int num);

    /**
     * 减少线程池中工作的线程的数量
     * @param num 需要减少的数量
     * @return
     */
    boolean removeWorkers(int num);
}

2.默认实现线程池

/**
 * 线程池接口的默认实现
 **/
public class DefaultThreadPool<Job extends Callable> implements ThreadPool<Job> {

    /**
     * 最大工作线程的数量
     */
    private static final int MAX_WORKER_NUMBERS = 10;

    /**
     * 默认的工作线程的数量
     */
    private static final int DEFAULT_WORKER_NUMBERS = 5;

    /**
     * 最小工作线程的数量
     */
    private static final int MIN_WORKER_NUMBERS = 1;

    /**
     * 任务线程队列
     */
    private LinkedList<Job> jobs = new LinkedList<>();

    /**
     * 工作线程队列,由于要进行增删操作,所以要使用线程安全的队列
     */
    private List<Worker> workers = Collections.synchronizedList(new ArrayList<>());

    private AtomicLong threadMarker = new AtomicLong(0);

    public DefaultThreadPool() {
        initWorkers(DEFAULT_WORKER_NUMBERS);
    }

    /**
     * 构造函数
     * @param workerNum 工作线程的数量,最大10个,最小1个
     */
    public DefaultThreadPool(int workerNum) {
        int num = workerNum > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : workerNum;
        num = num < MIN_WORKER_NUMBERS ? MIN_WORKER_NUMBERS : num;
        initWorkers(num);
    }

    /**
     * 初始化工作线程
     * @param num 工作线程的数量
     */
    private void initWorkers(int num) {
        for(int i = 0; i < num; i++) {
            createWorker();
        }
    }

    /**
     * 将任务放入到线程池中进行执行
     *
     * @param job 继承了callable的任务
     * @return
     */
    @Override
    public boolean execute(Job job) {
        synchronized (jobs) {
            // 添加一个任务到任务队列尾部,并将一个工作线程唤醒
            jobs.addLast(job);
            // 不需要使用notifyAll,因为此次只是添加一个任务,只需要唤醒一个工作线程就可以了
            jobs.notify();
        }
        return true;
    }

    /**
     * 关闭线程池
     *
     * @return
     */
    @Override
    public boolean shutdown() {
        for (Worker worker : workers) {
            worker.shutdown();
        }
        synchronized (jobs) {
            jobs.notifyAll();
        }
        return true;
    }

    /**
     * 增加线程池中工作的线程的数量
     *
     * @param num 需要增加的数量
     * @return
     */
    @Override
    public boolean addWorkers(int num) {
        num = num < 1 ? 0 : num;
        int addNum = num + workers.size() > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS - workers.size() : num;
        if(addNum > 0) {
            for(int i = 0; i < addNum; i++) {
                createWorker();
            }
        }
        return true;
    }

    /**
     * 减少线程池中工作的线程的数量
     *
     * @param num 需要减少的数量
     * @return
     */
    @Override
    public boolean removeWorkers(int num) {
        int removeNum = num > workers.size() ? workers.size() : num;
        removeNum = workers.size() - removeNum < MIN_WORKER_NUMBERS ? workers.size() - MIN_WORKER_NUMBERS : removeNum;
        for(int i = 0; i < removeNum; i++) {
            Worker worker = workers.get(i);
            workers.remove(worker);
            worker.shutdown();
        }
        synchronized (jobs) {
            jobs.notifyAll();
        }
        return true;
    }

    /**
     * 创建一个工作线程,并且将工作线程放入到队列中管理,并启动工作线程
     */
    private void createWorker() {
        Worker worker = new Worker();
        workers.add(worker);
        Thread thread = new Thread(worker, "WorkerThread-Pool-" + threadMarker.incrementAndGet());
        thread.start();
    }

    /**
     * 工作线程
     */
    class Worker implements Runnable {

        private final Logger logger = LoggerFactory.getLogger(Worker.class);

        private volatile boolean running = true;

        @Override
        public void run() {
            logger.info("{}启动成功", Thread.currentThread().getName());
            while (running) {
                Job job = null;
                synchronized (jobs) {
                    // 如果任务队列为空,则将工作线程挂起,并且释放锁,工作线程从runnable状态变成waiting状态,进入到等待队列中,
                    // waiting状态等待其他线程唤醒后,从等待队列进入到阻塞队列,然后重新获取锁,然后解除waiting状态,进入runnable状态
                    while (running && jobs.isEmpty()) {
                        try {
                            logger.info("{}等待任务...", Thread.currentThread().getName());
                            jobs.wait();
                        } catch (InterruptedException e) {
                            // 如果该线程被外部线程打断,打断该线程,并且设置running=false优雅的推出线程
                            logger.info("{}等待被打断...", Thread.currentThread().getName());
                            Thread.currentThread().interrupt();
                            running = false;
                        }
                    }
                    if(!jobs.isEmpty()) {
                        logger.info("{}获取一个任务", Thread.currentThread().getName());
                        job = jobs.removeFirst();
                    }
                }
                if(Objects.nonNull(job)) {
                    try {
                        logger.info("{}正在执行一个任务", Thread.currentThread().getName());
                        job.call();
                        logger.info("{}执行完成一个任务", Thread.currentThread().getName());
                    } catch (Exception e) {
                        // 忽略本次执行的异常,不然就会出现一次任务线程影响当前工作线程的情况
                    }
                }
            }
        }

        /**
         * 优雅的关闭线程
         * 将running设置成false,等待工作线程执行完当前任务,就不再执行新的任务,然后退出线程
         */
        public void shutdown() {
            running = false;
        }
    }
}

3.待实现...

根据一定的设计模式,配置化管理,参数化设计,功能设计等等,可以极大的扩展线程池的功能

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值