• 四种线程池


    1 FixedThreadPool

    FixedThreadPool是复用固定数量的线程处理一个共享的无边界队列,其定义如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    corePoolSize 和 maximumPoolSize都设置为创建FixedThreadPool时指定的参数nThreads,由于该线程池是固定线程数的线程池,当线程池中的线程数量等于corePoolSize 时,如果继续提交任务,该任务会被添加到阻塞队列workQueue中,而workQueue使用的是LinkedBlockingQueue,但没有设置范围,那么则是最大值(Integer.MAX_VALUE),这基本就相当于一个无界队列了。

     案例:

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    
    public class FixedThreadPoolTest {
        public static void main(String[] args) {
            ExecutorService service= Executors.newFixedThreadPool(3);
    
            for (int i = 0; i < 5; i++) {
                service.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"执行");
                });
            }
         exec.shutdown();
    } }

    2 SingleThreadExecutor

    SingleThreadExecutor只会使用单个工作线程来执行一个无边界的队列。

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    作为单一worker线程的线程池,它把corePool和maximumPoolSize均被设置为1,和FixedThreadPool一样使用的是无界队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样。

    SingleThreadExecutor只会使用单个工作线程,它可以保证认为是按顺序执行的,任何时候都不会有多于一个的任务处于活动状态。注意,如果单个线程在执行过程中因为某些错误中止,新的线程会替代它执行后续线程。  

     案例:

    public class Demo9SingleThreadPoolCase {
        static int count = 0;
    ​
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 10; i++) {
                exec.execute(new Demo());
                Thread.sleep(5);
            }
            exec.shutdown();
        }
    ​
        static class Demo implements Runnable {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                for (int i = 0; i < 2; i++) {
                    count++;
                    System.out.println(name + ":" + count);
                }
            }
        }
    }

    3 CachedThreadPool

    CachedThreadPool会根据需要,在线程可用时,重用之前构造好的池中线程,否则创建新线程:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    它把corePool为0,maximumPoolSize为Integer.MAX_VALUE,这就意味着所有的任务一提交就会加入到阻塞队列中。因为线程池的基本大小设置为0,一般情况下线程池中没有程池,用的时候再创建。

    但是keepAliveTime设置60,unit设置为秒,意味着空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。阻塞队列采用的SynchronousQueue,这是是一个没有元素的阻塞队列。

    这个线程池在执行 大量短生命周期的异步任务时,可以显著提高程序性能。调用 execute 时,可以重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。

    但是这样就处理线程池会存在一个问题,如果主线程提交任务的速度远远大于CachedThreadPool的处理速度,则CachedThreadPool会不断地创建新线程来执行任务,这样有可能会导致系统耗尽CPU和内存资源,所以在使用该线程池是,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。

     案例:

    public class Demo9CachedThreadPoolCase {
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newCachedThreadPool();
            for (int i = 0; i < 10; i++) {
                exec.execute(new Demo());
                Thread.sleep(1);
            }
            exec.shutdown();
        }
    ​
        static class Demo implements Runnable {
            @Override
            public void run() {
                String name = Thread.currentThread().getName();
                try {
                    //修改睡眠时间,模拟线程执行需要花费的时间
                    Thread.sleep(1);
    ​
                    System.out.println(name + "执行完了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    4 ScheduledThreadPool

    Timer与TimerTask虽然可以实现线程的周期和延迟调度,但是Timer与TimerTask存在一些问题:

    • Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,会发生一些缺陷。
    • 如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行。
    • Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化

    为了解决这些问题,我们一般都是推荐ScheduledThreadPoolExecutor来实现。

    ScheduledThreadPoolExecutor,继承ThreadPoolExecutor且实现了ScheduledExecutorService接口,它就相当于提供了“延迟”和“周期执行”功能的ThreadPoolExecutor。

    ScheduledThreadPoolExecutor,它可另行安排在给定的延迟后运行命令,或者定期执行命令。需要多个辅助线程时,或者要求 ThreadPoolExecutor 具有额外的灵活性或功能时,此类要优于Timer。

    提供了四种构造方法:

        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                    new DelayedWorkQueue());
        }
    ​
        public ScheduledThreadPoolExecutor(int corePoolSize,
                                           ThreadFactory threadFactory) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                    new DelayedWorkQueue(), threadFactory);
        }
    ​
        public ScheduledThreadPoolExecutor(int corePoolSize,
                                           RejectedExecutionHandler handler) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                    new DelayedWorkQueue(), handler);
        }
    ​
    ​
        public ScheduledThreadPoolExecutor(int corePoolSize,
                                           ThreadFactory threadFactory,
                                           RejectedExecutionHandler handler) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                    new DelayedWorkQueue(), threadFactory, handler);
        }

    在ScheduledThreadPoolExecutor的构造函数中,我们发现它都是利用ThreadLocalExecutor来构造的,唯一变动的地方就在于它所使用的阻塞队列变成了DelayedWorkQueue。

    DelayedWorkQueue为ScheduledThreadPoolExecutor中的内部类,类似于延时队列和优先级队列。在执行定时任务的时候,每个任务的执行时间都不同,所以DelayedWorkQueue的工作就是按照执行时间的升序来排列,执行时间距离当前时间越近的任务在队列的前面,这样就可以保证每次出队的任务都是当前队列中执行时间最靠前的。

    案例:

    import java.util.Date;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ScheduledThreadPoolTest {
        public static void main(String[] args) {
            ScheduledExecutorService service= Executors.newScheduledThreadPool(2);
    
            System.out.println("程序开始时间:"+new Date());
            Runnable runnable=()->{
                System.out.println(new Date()+Thread.currentThread().getName());
            };
    
            service.schedule(runnable, 0, TimeUnit.SECONDS);
            service.schedule(runnable, 1, TimeUnit.SECONDS);
            service.schedule(runnable, 5, TimeUnit.SECONDS);
    
            service.shutdown();
        }
    }
  • 相关阅读:
    利用BitLocker和vhdx创建一个有加密的Win10系统
    macOS 10.12 任何来源
    Xcode 8 GM 编译缺失 /Users/usr/lib/libresolv.9.dylib
    基于inline-block的列表布局
    markdown 的基本操作
    easyui1.32 各种问题汇总
    angular笔记
    underscore 笔记
    我的问道游戏主题皮肤
    在bootstrap ace样式框架上修改的后台管理型模板(Tab页后台管理模板)
  • 原文地址:https://www.cnblogs.com/zhuyapeng/p/14354592.html
Copyright © 2020-2023  润新知