一个来自一线程序员对Java线程池的理解 手写线程池 带你翻过线程池这座山 升级第二版啦

第一版地址:https://blog.csdn.net/wandou9527/article/details/107769598

第二版对比第一版优化点:

  • 线程实现延迟创建,对内存更友好
  • 自定义线程池实现java.util.concurrent.Executor 接口,更加符合规范
  • submit方法改为execute方法,语义更贴切
  • 加入对任务(command)的非空判断

优化后又离完美线程池近了一步。

如果想玩转 Java 的多线程与高并发,线程池是你永远也绕不过的山。既然绕不过,我们就啃他,吃透线程池,玩转高并发。

阅读Jdk线程池源码发现,Jdk里的线程池实现的非常完善,有很多复杂的逻辑处理,所以造成代码较长,而且代码格式也不规范(ps. 请原谅我指点江山,人家可能有人家的实际原因),eg. if 后没有大括号;很多变量命名都是单字母,比如c、w等。。。

本文,精简了线程池的一些复杂逻辑,从主干功能逻辑出发,实现主干功能,我相信更有助于我们理解线程池,然后再一步步深入。

Jdk里的线程池

主要属性

private volatile int corePoolSize; //核心线程数
private volatile int maximumPoolSize; //最大线程数
private volatile long keepAliveTime; //存活时间
private final BlockingQueue<Runnable> workQueue; //任务等待队列
private volatile ThreadFactory threadFactory; //线程工厂
private volatile RejectedExecutionHandler handler; //拒绝策略

下面介绍一下线程池执行任务的流程,理解各个属性的意义。当一个线程池初始化,向线程池提交任务,线程池新建线程执行任务,随着线程创建,线程数逐渐增多,当达到 corePoolSize 线程池将不再新建线程,而是将任务放入任务等待队列 workQueue 。再持续向线程池提交任务,当等待队列满了,这时会继续新建线程,直到到达最大线程数 maximumPoolSize,如果还继续有任务到来,线程池无法处理,这时就启动拒绝策略。

这个过程我们可以以生活中的例子比喻一下。大致我们把线程池理解为理发店。那么流程就是:来了顾客开始理发,比如只有4个理发师4个座位,相当于核心线程。那么来了过多的顾客,理发师忙不过来就会先让你去等候区稍等排队等待,前面有理完发的会叫你,相当于等待队列。等待区满了呢?现实中理发店肯定不会拒绝顾客的啊,他可能让你先在外面等。但如果等待区每天都爆满,那么老板可能会考虑扩大店面,扩充理发师团队了。所以,这只是个大致的比喻。

自定义手写线程池

废话不说,我们上代码。

package com.wandou.demo.thread.post.threapool;

import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author liming
 * @date 2020/09
 * @description 自定义线程池
 * new功能
 * - 线程延迟创建(来任务才创建)
 * 欠缺功能:
 * - 队列满了后继续创建线程,直到达到最大线程数
 * - 拒绝策略
 * - 线程超时销毁
 */
public class MyThreadPool implements Executor {
    
    

    /**
     * 核心线程数(核心理发师数量)
     */
    private volatile int corePoolSize;
    /**
     * 任务等待队列(等待区座位数)
     */
    private final BlockingQueue<Runnable> workQueue;
    /**
     * 线程容器(理发师作业区)
     */
    private final HashSet<MyThreadPool.Worker> workers = new HashSet<>();

    private final AtomicInteger workerCount = new AtomicInteger(0);


    public MyThreadPool(int corePoolSize, BlockingQueue<Runnable> workQueue) {
    
    
        this.corePoolSize = corePoolSize;
        this.workQueue = workQueue;
    }

    /**
     * 运行,来顾客了,安排
     *
     * @param command
     * @return
     */
    @Override
    public void execute(Runnable command) {
    
    
        if (command == null) {
    
    
            throw new NullPointerException();
        }
        int count = workerCount.get();
        // 如果小于核心线程数,可以建立新线程
        if (count < corePoolSize) {
    
    
            if (addWorker(command, true)) {
    
    
                return;
            }
        }
        // 创建线程不成功,尝试放入任务队列
        if (workQueue.offer(command)) {
    
    
            int recheck = workerCount.get();
            if (recheck == 0) {
    
    
                addWorker(null, false);
            }
            return;
        }
        //拒绝
        throw new RejectedExecutionException("Task " + command.toString() +
                " rejected from " +
                this.toString());
    }


    private boolean addWorker(Runnable command, boolean core) {
    
    
        for (; ; ) {
    
    
            int count = workerCount.get();
            if (count >= corePoolSize) {
    
    
                return false;
            }
            // 线程数+1, 成功向下走
            if (workerCount.compareAndSet(count, count + 1)) {
    
    
                break;
            }
        }
        Worker worker = new Worker(command);
        final Thread thread = worker.thread;
        workers.add(worker);
        thread.start();
        return true;
    }


    // ----------------

    /**
     * 线程(理发师)
     */
    private class Worker implements Runnable {
    
    

        /**
         * 工作者运行的线程
         */
        final Thread thread;
        /**
         * 初始运行的任务,可能为 null
         */
        Runnable firstTask;

        /**
         * 创建一个工作者
         *
         * @param firstTask 第一个任务,可以为 null
         */
        Worker(Runnable firstTask) {
    
    
            this.firstTask = firstTask;
            this.thread = new Thread(this);
        }

        @Override
        public void run() {
    
    
            System.out.println("run");
            runWorker(this);
        }

        @Override
        public boolean equals(Object o) {
    
    
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Worker worker = (Worker) o;
            return Objects.equals(thread, worker.thread) &&
                    Objects.equals(firstTask, worker.firstTask);
        }

        @Override
        public int hashCode() {
    
    
            return Objects.hash(thread, firstTask);
        }
    }

    final void runWorker(Worker worker) {
    
    
        Runnable task = worker.firstTask;
        worker.firstTask = null;
        while (task != null || (task = getTask()) != null) {
    
    
            try {
    
    
                task.run();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                // 运行完毕
                task = null;
            }
        }
    }

    private Runnable getTask() {
    
    
        for (; ; ) {
    
    
            try {
    
    
                return workQueue.take();
            } catch (InterruptedException e) {
    
    
                System.out.println("InterruptedException!!!");
            }
        }
    }
}

测试代码:

package com.wandou.demo.thread.post.threapool;

import org.junit.Test;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author liming
 * @date 2020/9/17
 * @description
 */
public class MyThreadPoolDemo {
    
    

    private BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
    private MyThreadPool myThreadPool = new MyThreadPool(3, workQueue);
    
    @Test
    public void t1() throws Exception {
    
    
        AtomicInteger atomicInteger = new AtomicInteger(0);
        Runnable task = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
//                    Thread.sleep(1);
                    System.out.println(atomicInteger.incrementAndGet()
                            + "号顾客来理发,为其理发的理发师是:"
                            + Thread.currentThread().getName());
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 30; i++) {
    
    
            myThreadPool.execute(task);
        }
        Thread.sleep(5000);
        System.out.println("================================================");
        for (int i = 0; i < 30; i++) {
    
    
            myThreadPool.execute(task);
        }
        //让主线程阻塞等待
        System.in.read();
    }

}

测试结果:

测试结果

我们发现无论来多少顾客理发,始终是0、1、2理发师轮流工作。这就是线程池的作用,实现了线程的复用,节省了资源,提升了程序性能。

这版优化对线程池理解又进了一步,我将持续优化,写出和jdk相当的线程池。

猜你喜欢

转载自blog.csdn.net/wandou9527/article/details/108652435
今日推荐