手写线程池

线程池的实现原理

关于线程池原理,详情请看 线程池原理

线程池是一个典型的生产者-消费者模型。下图所示为线程池的实现原理:

  1. 调用方不断向线程池中提交任务; (生产者)
  2. 线程池中有一组线程,不断地从队列中取任务,(消费者)
  3. 线程池管理一个任务队列,对 异步任务进行缓冲 (缓冲区)

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

  1. 队列设置多长?
  2. 如果是无界的,调用方不断往队列中方任务,可能导致内存耗尽。如果是有界的,当队列满了之后,调用方如何处理?
  3. 线程池中的线程个数是固定的,还是动态变化的?
  4. 每次提交新任务,是放入队列?还是开新线程
  5. 当没有任务的时候,线程是睡眠一小段时间?还是进入阻塞?如果进入阻塞,如何唤醒?

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

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

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

手写线程池

定义一个线程池接口

首先,定义一个线程池接口,用来表示线程池应该具备哪些功能。

一个简单的线程池应该至少具备以下几个功能:

  1. 添加任务并执行
  2. 关闭线程池
  3. 强制关闭线程池
import java.util.List;

public interface ThreadPool {
    //提交任务到线程池中
    void execute(Runnable task);

    //优雅关闭线程
    void shutdown();

    //立即关闭
    List<Runnable> shutdownNow();
}

实现线程的池化管理

import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class WorkThread extends Thread {
	
	//传入 threads 方便删除线程
    private List<WorkThread> threads;
    // 空闲时长
    private long keepAliveTime;

    private BlockingQueue<Runnable> taskQueue;

    public WorkThread(long keepAliveTime,BlockingQueue<Runnable> taskQueue,List<WorkThread> threads){
        this.threads = threads;
        this.keepAliveTime = keepAliveTime;
        this.taskQueue = taskQueue;
    }

    @Override
    public void run() {
        long lastActiveTime = System.currentTimeMillis();
        Runnable task;
        while (!Thread.currentThread().isInterrupted() && !taskQueue.isEmpty()){
            try {
                // 从任务队列中取出一个任务,如果队列为空,则阻塞等待
                task = taskQueue.poll(keepAliveTime, TimeUnit.MILLISECONDS);
                if (task != null) {
                    task.run();
                    lastActiveTime = System.currentTimeMillis();
                } else if (System.currentTimeMillis() - lastActiveTime >= keepAliveTime) {
                    // 从线程池中移除
                    threads.remove(this);
                    System.out.printf("WorkerThread %d exit %n", Thread.currentThread().getId());
                    break;
                }
            } catch (InterruptedException e) {
                // 从线程池中移除
                threads.remove(this);
                e.printStackTrace();
                // 如果线程被中断,则退出循环
                break;
            }

        }
    }
}

在WorkerThread类run方法里,采用taskQueue.poll方法指定等待时长,这里是线程退出的关键。如果超时未获取到任务,则表明当前线程长时间未处理任务,可以正常退出,并从线程池里移除该线程。

自定义线程池的基本参数

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class SimpleThreadPool implements ThreadPool {

    //线程池初始化时的线程数量
    private int initSize;

    // 线程池最大线程数量
    private int maxSize;

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

    // 队列大小
    private int queueSize;

    // 线程空闲时长
    private long keepAliveTime;

    //任务队列
    private BlockingQueue<Runnable> taskQueue;

    //工作线程
    private List<WorkThread> threads;

    private volatile boolean isShutdown = false;

    public SimpleThreadPool(int initSize, int maxSize, int coreSize, int queueSize, long keepAliveTime) {
        this.initSize = initSize;
        this.maxSize = maxSize;
        this.coreSize = coreSize;
        this.queueSize = queueSize;
        this.keepAliveTime = keepAliveTime;
        taskQueue = new LinkedBlockingQueue<>(queueSize);
        threads = new ArrayList<>(initSize);
        // 初始化方法,创建一定数量的工作线程,并启动它们
        for (int i = 0; i < initSize; i++) {
            WorkThread workerThread = new WorkThread(keepAliveTime, taskQueue, threads);
            workerThread.start();
            threads.add(workerThread);
        }
    }


    //将任务添加到队列中
    @Override
    public void execute(Runnable task) {
        if (isShutdown) {
            throw new IllegalStateException("ThreadPool is shutdown");
        }
        // 当线程数量小于核心线程数时,创建新线程
        if (threads.size() < coreSize) {
            addWorkerThread(task);
        } else if (!taskQueue.offer(task)) {
            // 当队列已满时,且线程数量小于最大线程数量时,创建新的线程
            if (threads.size() < maxSize) {
                addWorkerThread(task);
            } else {
                throw new IllegalStateException("执行任务失败");
            }
        }
    }

    // 创建新的线程,并执行任务
    private void addWorkerThread(Runnable task) {
        WorkThread workerThread = new WorkThread(keepAliveTime, taskQueue, threads);

        threads.add(workerThread);
        // 任务放入队列
        taskQueue.offer(task);
    }

    //关闭线程池
    @Override
    public void shutdown() {
        // 修改状态
        isShutdown = true;
        for (WorkThread thread : threads) {
            // 中断线程
            thread.interrupt();
        }
    }

    @Override
    public List<Runnable> shutdownNow() {
        // 修改状态
        isShutdown = true;
        // 清空队列
        List<Runnable> remainingTasks = new ArrayList<>();
        taskQueue.drainTo(remainingTasks);
        // 中断所有线程
        for (WorkThread thread : threads) {
            thread.interrupt();
        }
        // 返回未执行任务集合
        return remainingTasks;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值