zoukankan      html  css  js  c++  java
  • 线程池的种类和基本使用实例

    线程池的继承架构

      程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

      线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

    在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

     

      Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。

    真正的线程池接口是ExecutorService。下面这张图完整描述了线程池的类体系结构。

      Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;

      然后ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;

      抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;

      然后ThreadPoolExecutor继承了类AbstractExecutorService。

    标记一下比较重要的类:

    ExecutorService:

    真正的线程池接口。

    ScheduledExecutorService

    能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

    ThreadPoolExecutor

    ExecutorService的默认实现。

    ScheduledThreadPoolExecutor

    继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,

    因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

     newSingleThreadExecutor

      创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。

      如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    newFixedThreadPool

      创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。

      线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

    newCachedThreadPool

      创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,

      当任务数增加时,此线程池又可以智能的添加新线程来处理任务。

      此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    newScheduledThreadPool

      创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

    示例

    1. newFixedThreadPool:定长线程池

      每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试创建定长线程池
     * @author ConstXiong
     */
    public class TestNewFixedThreadPool {
    
        public static void main(String[] args) {
            //创建工作线程数为 3 的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
            //提交 6 个任务
            for (int i = 0; i <6; i++) {
                final int index = i;
                fixedThreadPool.execute(() -> {
                    try {
                        //休眠 3 秒
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " index:" + index);
                });
            }
            
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("4秒后...");
            
            //关闭线程池后,已提交的任务仍然会执行完
            fixedThreadPool.shutdown();
        }
        
    }

     打印结果:

     

     2. newCachedThreadPool,创建可缓存的线程池

      如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试创建可缓存的线程池
     * @author ConstXiong
     */
    public class TestNewCachedThreadPool {
        
        public static void main(String[] args) {
            //创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    
            for (int i = 0; i <6; i++) {
                final int index = i;
                cachedThreadPool.execute(() -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " index:" + index);
                });
            }
            
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("4秒后...");
            
            cachedThreadPool.shutdown();
            
        }
        
    }

    打印结果可以看出,创建的线程数与任务数相等

     3. newScheduledThreadPool,创建定长线程池,可执行周期性的任务。

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 测试创建定长线程池,可执行周期性的任务
     * @author ConstXiong
     */
    public class TestNewScheduledThreadPool {
    
        public static void main(String[] args) {
            //创建定长线程池,可执行周期性的任务
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
            
            for (int i = 0; i <3; i++) {
                final int index = i;
                //scheduleWithFixedDelay 固定的延迟时间执行任务; scheduleAtFixedRate 固定的频率执行任务
                scheduledThreadPool.scheduleWithFixedDelay(() -> {
                        System.out.println(Thread.currentThread().getName() + " index:" + index);
                }, 0, 3, TimeUnit.SECONDS);
            }
            
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("4秒后...");
            
            scheduledThreadPool.shutdown();
    
        }
    }

    打印结果:

    4. newSingleThreadExecutor,创建单线程的线程池

    线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试单线程的线程池
     * @author ConstXiong
     */
    public class TestNewSingleThreadExecutor {
        
        public static void main(String[] args) {
            //单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
            ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
            
            //提交 3 个任务
            for (int i = 0; i <3; i++) {
                final int index = i;
                singleThreadPool.execute(() -> {
                    
                    //执行第二个任务时,报错,测试线程池会创建新的线程执行任务三
                    if (index == 1) {
                        throw new RuntimeException("线程执行出现异常");
                    }
                    
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " index:" + index);
                });
            }
            
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("4秒后...");
            
            singleThreadPool.shutdown();
        }
    
    }

    打印结果可以看出,即使任务出现了异常,线程池还是会自动补充一个线程继续执行下面的任务

    5. newSingleThreadScheduledExecutor,创建单线程可执行周期性任务的线程池。

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 测试单线程可执行周期性任务的线程池
     * @author ConstXiong
     */
    public class TestNewSingleThreadScheduledExecutor {
    
        public static void main(String[] args) {
            //创建单线程可执行周期性任务的线程池
            ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
            
            //提交 3 个固定频率执行的任务
            for (int i = 0; i <3; i++) {
                final int index = i;
                //scheduleWithFixedDelay 固定的延迟时间执行任务; scheduleAtFixedRate 固定的频率执行任务
                singleScheduledThreadPool.scheduleAtFixedRate(() -> {
                    System.out.println(Thread.currentThread().getName() + " index:" + index);
                }, 0, 3, TimeUnit.SECONDS);
            }
            
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("4秒后...");
            
            singleScheduledThreadPool.shutdown();
        }
        
    }

    打印机结果可以看出 0-2 任务都被执行了 2 个周期

    6. newWorkStealingPool,创建任务可窃取线程池

    空闲线程可以窃取其他任务队列的任务,不保证执行顺序,适合任务耗时差异较大。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试可任务窃取线程池
     * @author ConstXiong
     */
    public class TestNewWorkStealingPool {
    
        public static void main(String[] args) {
            //创建 4个工作线程的 任务可窃取线程池,如果不设置并行数,默认取 CPU 总核数
            ExecutorService workStealingThreadPool = Executors.newWorkStealingPool(4);
            
            for (int i = 0; i <10; i++) {
                final int index = i;
                workStealingThreadPool.execute(() -> {
                    try {
                        //模拟任务执行时间为 任务编号为0 1 2 的执行时间需要 3秒;其余任务200 毫秒,导致任务时间差异较大
                        if (index <= 2) {
                            Thread.sleep(3000);
                        } else {
                            Thread.sleep(200);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " index:" + index);
                });
            }
            
            try {
                Thread.sleep(10000);//休眠 10 秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("10秒后...");
        }
        
    }

    打印结果可以看出,线程 ForkJoinPool-1-worker-0 把3-9的任务都执行完

  • 相关阅读:
    Fruit Ninja(随机数rand())
    C. A Mist of Florescence ----- Codeforces Round #487 (Div. 2)
    给力的移动 FZU
    FZU 2254 英语考试 (最小生成树)
    6486: An Ordinary Game(规律)
    HDU 1114: Piggy-Bank
    HDU 5916: Harmonic Value Description
    1072 威佐夫游戏
    1069 Nim游戏
    1066 Bash游戏
  • 原文地址:https://www.cnblogs.com/FondWang/p/12416460.html
Copyright © 2011-2022 走看看