手写线程池(简化版)

前言
要想用好线程池,就得掌握其原理,所谓磨刀不误砍柴工,深入了解线程池的工作原理,对日常工作开发,最重要的是应付面试。以前基本看了一一段时间就忘记了,究其根本还是没有理解性记忆,废话不说,我们来手写一个简化版的线程池,彻底掌握线程池的基本原理吧

一、写在前面 队列的基本方法

        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1)
        try {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    System.out.println("测试");
                }
            };
            // 队列有任务,返回任务,没有任务等待一段时间,依然没任务就返回null
            workQueue.poll(11,TimeUnit.SECONDS);
            // 队列有任务,返回任务,没有任务阻塞等待 知道队列当中有任务
            workQueue.take();
            // 往队列当中添加任务,成功添加返回true,返回false说明,队列满了
            workQueue.offer(task);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

二、线程池基本流程

通过executor(task),添加一个任务,首先会判断当前线程数是否小于核心线程数,如果小于核心线程数,则创建核心线程并添加任务到该线程,如何超过了核心线程数,则添加到任务列队中;如果任务队里满了,就判断线程数是否小于最大线程数,如果小于,则创建非核心线程;如果不小于,则执行拒绝策略
手写线程池(简化版)_第1张图片

三、线程池的基本参数

    // 核心线程池的数量
    private int corePoolCount;
    // 最大线程池的数量
    private int maxPoolCount;
    // 非核心线程的空闲后的存活时间
    private int keepAliveTime;
    // 非核心线程的空闲后的存活时间 单位 暂不实现,默认为秒s
    // private int TimeUnit unit;
    // 队列 线程安全
    BlockingQueue<Runnable> workQueue;
    // 线程工厂,暂时不实现
    // ThreadFactory threadFactory;
    // 拒绝策略,暂时不实现,我们采用抛异常的方式
    //RejectedExecutionHandler handler;
	
	// 构造方法初始化这些内部变量
	public MyThreadPoolExecutor(int corePoolCount, 
	    						int maxPoolCount, 
	    						int keepAliveTime, 
	    						BlockingQueue<Runnable> workQueue) {
        this.corePoolCount = corePoolCount;
        this.maxPoolCount = maxPoolCount;
        this.keepAliveTime = keepAliveTime;
        this.workQueue = workQueue;
    }

四、线程池的其他内部变量

    // 当前线程的状态
    private AtomicInteger status = new AtomicInteger();
    // 当前工作线程的数量
    private AtomicInteger workerCount = new AtomicInteger();
    // 当前工作线程
    private HashSet<Worker> workers = new HashSet<>();
    // 由于hashSet 线程不安全所以我们增加一个锁
    private final Lock L =new ReentrantLock();
    // 线程的状态有 总共有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED 本文先实现两种
    // 启动状态
    private final static Integer RUNNING = 0;
    // 停止状态
    private final static Integer STOP = 1;
    // 获取当前完成的总任务数
    private int finishedTaskCountTotal=0;
	// 工作线程 内部类
    private final class  Worker implements Runnable{
        private Thread thread;
        private Runnable firstTask;
        // 记录当前工作线程完成的任务情况
        private int finishTaskCount=0;
        public Worker(Runnable firstTask){
            this.firstTask = firstTask;
            this.thread = new Thread(this);
        }
        @Override
        public void run() {
            // 方法暂未实现,后面会实现 此处里一个flag1
            runWorker(this);
        }
    }

五、线程池的execute方法

    public void execute(Runnable task) {
        if(task==null){
            throw new NullPointerException("别搞个空的过来");
        }
        if(status.get()==STOP) throw new RuntimeException("不能添加新任务了");

        if(status.get() == RUNNING){
            // 小于核心线程数量
            if(workerCount.get()<corePoolCount&& addWorker(task,true)){
                return;
            }
            // 核心线程满了,是不是就添加到工作队列里
            // 队列本身线程安全
            if(workQueue.offer(task)){
                // 成功放入队列 返回
                return;
            }
            // 到此处,说明可以添加非核心线程
            if(workerCount.get()<maxPoolCount && addWorker(task,false)){
                return;
            }
            // 创建不了执行拒绝策略
            throw new RuntimeException("拒绝策略");
        }
    }

六、添加工作线程addWorker

// 添加工作线程
    private boolean addWorker(Runnable task, boolean core) {
        if(status.get()==STOP){
            return false;
        }
        retry:
        while (true){
            if(status.get()==STOP){
                return false;
            }
            while (true){
                // 想创建工作线程,但是已经 创建完了
                if(workerCount.get()>=(core?corePoolCount:maxPoolCount)){
                    return false;
                }
                // 到这里,说明我们可以创建一个工作线程了
                if(!casAddWorkerCount()){
                    // 自增失败,可能是因为高并发,导致的原子操作失败了
                    continue retry;
                }
                // 说明自增成功了,跳出双层循环
                break retry;
            }
        }
        Worker worker = null;
        try {
            L.lock();// 次数 操作workers 需要加锁,因为hashset线程不安全
            worker =new Worker(task);
            Thread thread = worker.thread;
            if(thread!=null){
                if(thread.isAlive()){
                    // 说明这个线程,不是我启动的,一般不会出现这种情况,源码的严谨
                    throw new IllegalStateException();
                }
            }
            // start 方法的含义:开启一个新线程,在这个线程里面执行task的run方法 此处就会执行
            // 上面的runWorker(this) 就是上面的flag1
            thread.start();
            workers.add(worker);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            L.unlock();
        }
        return true;
    }

七、runWorker的具体实现

    private void runWorker(Worker worker) {
        if(worker==null) throw new NullPointerException("空的工作线程");
        try {
            Runnable firstTask = worker.firstTask;
            Thread wk = worker.thread;
            worker.firstTask = null;
            // 当前的任务先给他完成了
            while(firstTask!=null || (firstTask=getTask())!=null){
                // 如果执行线程池的stop方法,所有工作队里都要进行interrupted
                if(wk.isInterrupted()){
                    System.out.println("this thread is interrupted");
                }
                if(status.get()==STOP){
                    System.out.println("this threadPoll has already stopped");
                }

                firstTask.run();
                firstTask = null;
                worker.finishTaskCount++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 没任务了退出
            while (true){
                // 原子类的操作有可能失败,所以要不停重试,直到成功为止
                if(casDeleteWorkerCount()){
                    finishedTaskCount += worker.finishTaskCount;
                    break;
                }else{
                    continue;
                }
            }
            L.lock();
            try {
                workers.remove(worker);
            } finally {
                L.unlock();
            }
        }
    }

八、getTask方法

    private Runnable getTask() {
        Boolean timeOut = false;
        try {
            Runnable task = null;
            while (true){
                if(timeOut){
                   return null;
                }
                if(status.get()==STOP) return null;
                // 工作线程小于等于核心线程数
                if(workerCount.get()<=corePoolCount){
                    // 常驻
                    task = workQueue.take();
                }else{
                    // 非核心线程
                    // 非常驻,等待一段时间,还没拿到就返回了
                    task = workQueue.poll(keepAliveTime, TimeUnit.SECONDS);
                }
                if(task!=null){
                    return task;
                }
                timeOut = true;
            }
        } catch (InterruptedException e) {
            return null;
        }
    }

九、工作线程数原子类workerCount的加、减操作

    private boolean casAddWorkerCount() {
        return workerCount.compareAndSet(workerCount.get(),workerCount.get()+1);
    }

    private boolean casDeleteWorkerCount() {
        return  workerCount.compareAndSet(workerCount.get(),workerCount.get()-1);
    }

十、线程池的shutdown方法

    public void shutdown() {
        L.lock();
        try {
            setState(STOP);
            interruptAllWorkers();
        } finally {
            L.unlock();
        }
    }
    private void interruptAllWorkers() {
        L.lock();
        try {
            for (Worker worker : workers) {
                if(!worker.thread.isInterrupted()){
                    worker.thread.interrupt();
                }
                this.finishedTaskCountTotal+=worker.finishTaskCount;
            }
        } finally {
            L.unlock();
        }
    }

    private void setState(Integer stop) {
        if(status.get()==stop){
            return;
        }
        while(true){
            if(status.get()==stop){
                break;
            }
            if(status.compareAndSet(status.get(),STOP)){
                break;
            }
        }
    }

十一、测试

    public static void main(String[] args) throws InterruptedException {
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(1,5,200,new ArrayBlockingQueue<>(15));
        for (int i = 0; i < 20; i++) {
            final int s =i;
            myThreadPoolExecutor.execute(()->{
                System.out.println("打印"+s);
            });
        }
        TimeUnit.SECONDS.sleep(2);
    }

写在最后

本文只是从一个java新手的角度,尝试梳理线程池的大致流程以及原理帮助自己梳理清楚线程池,还有很多未实现的,线程池的不同状态有不同的处理方式,还有shutdown和shutdownnow的区别,等等大家可以从源码当中找到答案。

你可能感兴趣的:(java基础,java,面试)