• 关于线程池,那些你还不知道的事


    一、背景

      最近在学习线程相关的知识,然后顺理成章少不了学习线程池,刚开始在没有深入的学习之前,感觉线程池是很神秘的东西,而且完全想不到怎么才能实现一个自己的线程池,然后还能保证它的可用性,然后就一直琢磨,琢磨了一周才不多,也是网上各种查资料,终于明白了线程池的原理,也自己手写一个线程池,来加深印象,那么本文我们就来聊一聊关于线程池的知识,希望更多的猿友能看到,从此对线程池有一个清晰直观的认识。

    二、概念解析

    1.什么是线程池

      线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

    2.使用线程池的好处

      合理的使用线程池可以重复利用已创建的线程,这样就可以减少在创建线程和销毁线程上花费的时间和资源。并且,线程池在某些情况下还能动态的调整工作线程的数量,以平衡资源消耗和工作效率。同时线程池还提供了对池中工作线程进行统一的管理的相关方法。这样就相当于我们一次创建,就可以多次使用,大量的节省了系统频繁的创建和销毁线程所需要的资源。

    3.线程池的主要组件

    一个线程池包括以下四个基本组成部分:
    (1)、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
    (2)、工作线程(WorkThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
    (3)、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
    (4)、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

    4.JDK中线程池常用类UML类关系图

    三、手写实现

    我们知道了线程池的原理以及主要组件之后,就让我们来手动实现一个自己的线程池,以加深理解和深入学习。

    1.线程池接口类

    package com.hafiz.proxy.threadPool;
    
    import java.util.List;
    
    /**
     * Desc:线程池接口类
     * Created by hafiz.zhang on 2017/9/19.
     */
    public interface ThreadPool {
    
        // 执行单个线程任务
        void execute(Runnable task);
    
        // 执行多个任务
        void execute(Runnable[] tasks);
    
        // 执行多个任务
        void execute(List<Runnable> tasks);
    
        // 返回已经执行的任务个数
        int getExecuteTaskNumber();
    
        // 返回等待被处理的任务个数,队列的长度
        int getWaitTaskNumber();
    
        // 返回正在工作的线程的个数
        int getWorkThreadNumber();
    
        // 关闭线程池
        void destroy();
    }

    2.线程池实现类ThreadPoolManager.java

    package com.hafiz.proxy.threadPool;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Queue;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.atomic.AtomicLong;
    
    /**
     * Desc:线程池实现类
     * Created by hafiz.zhang on 2017/9/19.
     */
    public class ThreadPoolManager implements ThreadPool {
    
        // 线程池中默认线程的个数为5
        private static Integer workerNum = 5;
    
        // 工作线程数组
        WorkThread[] workThreads;
    
        // 正在执行的线程任务数量
        private static volatile Integer executeTaskNumber = 0;
    
        // 任务队列, 作为一个缓冲
        private Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();
    
        // 单例模式
        private static ThreadPoolManager threadPool;
    
        private AtomicLong threadNum = new AtomicLong();
    
        private ThreadPoolManager() {
            this(ThreadPoolManager.workerNum);
        }
    
        private ThreadPoolManager(int workerNum) {
            if (workerNum > 0) {
                ThreadPoolManager.workerNum = workerNum;
            }
            workThreads = new WorkThread[ThreadPoolManager.workerNum];
            for (int i = 0; i < ThreadPoolManager.workerNum; i++) {
                workThreads[i] = new WorkThread();
                Thread thread = new Thread(workThreads[i], "ThreadPool-worker-" + threadNum.incrementAndGet());
                thread.start();
                System.out.println("初始化线程总数:" + (i+1) + ",当前线程名称是:ThreadPool-worker-" + threadNum);
            }
        }
    
        public static ThreadPool getThreadPool() {
            return getThreadPool(workerNum);
        }
    
        public static ThreadPool getThreadPool(int workerNum) {
            if (workerNum > 0) {
                ThreadPoolManager.workerNum = workerNum;
            }
            if (threadPool == null) {
                threadPool = new ThreadPoolManager(ThreadPoolManager.workerNum);
            }
            return threadPool;
        }
    
    
        @Override
        public void execute(Runnable task) {
            synchronized (taskQueue) {
                taskQueue.add(task);
                taskQueue.notifyAll();
            }
        }
    
        @Override
        public void execute(Runnable[] tasks) {
            execute(Arrays.asList(tasks));
        }
    
        @Override
        public void execute(List<Runnable> tasks) {
            synchronized (taskQueue) {
                for (Runnable task : tasks) {
                     taskQueue.add(task);
                }
                taskQueue.notifyAll();
            }
        }
    
        @Override
        public String toString() {
            return "ThreadPoolManager{" +
                    "当前的工作线程数量=" + getWorkThreadNumber() +
                    ", 已完成的任务数=" + getExecuteTaskNumber() +
                    ", 等待任务数=" + getWaitTaskNumber() +
                    '}';
        }
    
        @Override
        public int getExecuteTaskNumber() {
            return executeTaskNumber;
        }
    
        @Override
        public int getWaitTaskNumber() {
            return taskQueue.size();
        }
    
        @Override
        public int getWorkThreadNumber() {
            return workerNum;
        }
    
        @Override
        public void destroy() {
            while (!taskQueue.isEmpty()) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < workThreads.length; i++) {
                workThreads[i].shutdown();
                workThreads[i] = null;
            }
            threadPool = null;
            taskQueue.clear();
        }
    
        private class WorkThread implements Runnable {
            // 线程是否可用
            private boolean isRunning = true;
    
            @Override
            public void run() {
                Runnable r = null;
                while (isRunning) {
                    // 队列同步机制,加锁
                    synchronized (taskQueue) {
                        while (isRunning && taskQueue.isEmpty()) {
                            try {
                                taskQueue.wait(20);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        if (!taskQueue.isEmpty()) {
                            r = taskQueue.poll();
                        }
                    }
                    if (r != null) {
                        r.run();
                    }
                    executeTaskNumber++ ;
                    r = null;
                }
            }
    
            public void shutdown() {
                isRunning = false;
            }
        }
    }

    其中该类中包含内部类WorkThread,它用来包装真正的线程类,给每一个线程一个是否可用的标志,该线程工作室同步的从taskQueue中取出要执行的任务进行调用run方法来执行任务。

    这个类中的getThreadPool方法中我们还使用到了懒汉式来实现单例,单例模式也是Java常用设计模式之一。

    注意该类中的destroy方法的实现:我们是一直等到队列中的所有的任务执行完毕,才真正的销毁线程池,销毁的过程中不要忘记将每一个线程对象置为null,并且清空任务队列,这样更利于java的垃圾回收。

    3.自定义任务类Task.java

    package com.hafiz.proxy.threadPool;
    
    /**
     * Desc:自定义任务类
     * Created by hafiz.zhang on 2017/9/21.
     */
    public class Task implements Runnable {
    
        private static volatile Integer i = 1;
    
        @Override
        public void run() {
            // 执行任务
            synchronized (i) {
                System.out.println("当前处理的线程是:" + Thread.currentThread().getName() + ",执行任务:" + (i++) + "完成");
            }
        }
    }

    4.线程池测试类

    package com.hafiz.proxy.threadPool;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Desc:线程池测试类
     * Created by hafiz.zhang on 2017/9/20.
     */
    public class ThreadPoolTest {
        public static void main(String[] args) {
            ThreadPool t = ThreadPoolManager.getThreadPool(6);
            List<Runnable> tasks = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                tasks.add(new Task());
            }
            System.out.println(t);
            t.execute(tasks);
            // 所有的线程执行完成才destroy
            t.destroy();
            System.out.println(t);
        }
    }

    5.测试结果:(为了篇幅,只创建10个任务运行)

    四、总结

      通过本文,我们弄明白线程池到底是怎么工作,学习知识的过程中,我们就是要知其然知其所以然。这样我们才能更好地驾驭它,才能更好地去理解和使用,也能更好地帮助我们触类旁通,后面有机会我们接着来说数据库连接池的原理及手写实现。

  • 相关阅读:
    js文件内部导入引用js文件方法
    CSS3.0动画之hover---Y轴----3D旋转
    静态的html页面想要设置使用浏览器缓存
    opener 属性是一个可读可写的属性,可返回对创建该窗口的 Window 对象的引用
    input获取永久焦点
    修改zepto源代码,使支持wp8的ie10
    EChart
    input属性disabled和readonly的区别
    trigger
    解决jquery mobile的遇到高版本Chrome一直转圈,页面加载不出来的情况。
  • 原文地址:https://www.cnblogs.com/hafiz/p/7589352.html
Copyright © 2020-2023  润新知