zoukankan      html  css  js  c++  java
  • java基础:简单实现线程池

    前段时间自己研究了下线程池的实现原理,通过一些源码对比,发现其实核心的东西不难,于是抽丝剥茧,决定自己实现一个简单线程池,当自已实现了出一个线程池后。发现原来那么高大上的东西也可以这么简单。

    先上原理图:为了更好的在手机上显示,我重新把图画了一遍

    线程池简单实现

    上代码之前,要先补充一下线程池构造的核心几个点

    1. 线程池里的核心线程数与最大线程数
    2. 线程池里真正工作的线程worker
    3. 线程池里用来存取任务的队列BlockingQueue
    4. 线程中的任务task

    本例实现简化了一些,只实现了BlockingQueue存放任务,然后每个worker取任务并执行,下面看代码
    首先定义一个线程池ThreadExcutor

    class ThreadExcutor{
    
        //创建
        private volatile boolean RUNNING = true;
    
        //所有任务都放队列中,让工作线程来消费
        private static BlockingQueue<Runnable> queue = null;
    
        private final HashSet<Worker> workers = new HashSet<Worker>();
    
        private final List<Thread> threadList = new ArrayList<Thread>();
    
        //工作线程数
        int poolSize = 0;
        //核心线程数(创建了多少个工作线程)
        int coreSize = 0;
    
        boolean shutdown = false;
    
        public ThreadExcutor(int poolSize){
            this.poolSize = poolSize;
            queue = new LinkedBlockingQueue<Runnable>(poolSize);
        }
    
        public void exec(Runnable runnable) {
            if (runnable == null) throw new NullPointerException();
            if(coreSize < poolSize){
                addThread(runnable);
            }else{
                //System.out.println("offer" +  runnable.toString() + "   " + queue.size());
                try {
                    queue.put(runnable);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void addThread(Runnable runnable){
            coreSize ++;
            Worker worker = new Worker(runnable);
            workers.add(worker);
            Thread t = new Thread(worker);
            threadList.add(t);
            try {
                t.start();
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    
        public void shutdown() {
            RUNNING = false;
            if(!workers.isEmpty()){
                for (Worker worker : workers){
                    worker.interruptIfIdle();
                }
            }
            shutdown = true;
            Thread.currentThread().interrupt();
        }
       //这里留个位置放内部类Worker
     }
    

    然后定义一个内部类Worker,这个内部类Worker是用来执行每个任务的,在创建线程池后,往线程里添加任务,每个任务都是由Worker一个一个来启动的。

        /**
         * 工作线程
         */
        class  Worker implements Runnable{
    
            public Worker(Runnable runnable){
                queue.offer(runnable);
            }
    
            @Override
            public void run() {
                while (true && RUNNING){
                    if(shutdown == true){
                        Thread.interrupted();
                    }
                    Runnable task = null;
                    try {
                        task = getTask();
                        task.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            public Runnable getTask() throws InterruptedException {
                return queue.take();
            }
    
            public void interruptIfIdle() {
                for (Thread thread :threadList) {
                    System.out.println(thread.getName() + " interrupt");
                    thread.interrupt();
                }
            }
        }
    

    首先注意的一点,这个Worker是个内部类,是在线程池内声明的。

    exec方法

    只要将任务放到线程池定义的队列中就行了

    Worker怎么工作

    任劳任怨的Worker

    这个工作线程实例化的时候就先加入一个任务到队列中,也就是说在实例化这个工作线程时,这个工作线程也是一个任务被加入到线程池中。然后就是run方法,这个run方法是线程调start方法生成的线程,而Worker调的run方法并没有生成新的线程。就是一个循环,一直在不停的从队列中取任务,然后执行。可以看到,取队列的方法是take(),这个方法意思如果队列为空了,取不到数据时就阻塞队列。

    然后看shutdown()

    shutdown动作迅速

    你每天辛勤的劳动着,突然接收到上面的命令,说活暂时不要接了,先停下来,当你还没搞清楚状况时,接着你的领导又把你开除了,说公司要倒了,你先下岗吧,一会我也得下岗了。这就是shutdown做的事,shutdown必须是主线程才能停止工作线程。
    shutdown方法并不是用线程那种强制停止的搞法,而是先用一个标识符告诉工作线程,不要再接任务了。然后通知工作线程,你可以interrupt()了,当所有的线程停止后记得要把主线程也停掉,这样,一个简单任务的线程池就完成了。
    让我们来测试一下:

    /**
     * Created by wxwall on 2017/6/7.
     */
    public class TheadBlockedQ {
        public static void main(String[] args) throws InterruptedException {
            ThreadExcutor excutor = new ThreadExcutor(3);
            for (int i = 0; i < 10; i++) {
                excutor.exec(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("线程 " + Thread.currentThread().getName() + " 在帮我干活");
                    }
                });
            }
           excutor.shutdown();
        }
    }
    

    输出结果为:

    线程 Thread-0 在帮我干活
    线程 Thread-2 在帮我干活
    线程 Thread-1 在帮我干活
    线程 Thread-0 在帮我干活
    线程 Thread-2 在帮我干活
    线程 Thread-2 在帮我干活
    线程 Thread-1 在帮我干活
    线程 Thread-0 在帮我干活
    Thread-0 interrupt
    Thread-1 interrupt
    Thread-2 interrupt
    Thread-0 interrupt
    Thread-1 interrupt
    Thread-2 interrupt
    Thread-0 interrupt
    Thread-1 interrupt
    Thread-2 interrupt
    

    这当然是最简单实现,JDK的实现比这强大的多,而且还具备当工作线程处理不过来时,可以产生新的线程来处理任务,这个数量不能超过原先定义的最大线程数,而在本例中都没实现这些功能。
    我相信当想了解一个模块的功能时,如果一开始就了解其中最核心的点,然后向外慢慢扩展,那么学习这个模块时一定能省下不少时间,而且理解将很深刻。希望这个简单线程池实现能让你有所领悟,以更加简单的方式了解线程池,了解了线程池,对于其他池化技术,原理都是相通的。

    最后我想说:我相信写好一篇文章能让大家理解不困难都是用了心的,您也点个赞支持支持。

  • 相关阅读:
    HDU 5640 King's Cake
    HDU 5615 Jam's math problem
    HDU 5610 Baby Ming and Weight lifting
    WHU1604 Play Apple 简单博弈
    HDU 1551 Cable master 二分
    CodeForces659C Tanya and Toys map
    Codeforces 960E 树dp
    gym 101485E 二分匹配
    Codeforces 961E 树状数组,思维
    Codeforces Round #473 (Div. 2) D 数学,贪心 F 线性基,模板
  • 原文地址:https://www.cnblogs.com/wxwall/p/7050698.html
Copyright © 2011-2022 走看看