zoukankan      html  css  js  c++  java
  • 多线程:四大线程池详解

    new Thread 的弊端

    首先看一段代码:

    /**
     * Created by Zero on 2017/8/30.
     */
    public class ThreadTest {
        public static void main(String[] args) {
            while (true) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread());
                    }
                }).start();
            }
        }
    }
    Thread[Thread-0,5,main]
    Thread[Thread-4,5,main]
    Thread[Thread-3,5,main]
    Thread[Thread-6,5,main]
    Thread[Thread-7,5,main]
    Thread[Thread-2,5,main]
    Thread[Thread-1,5,main]
    Thread[Thread-8,5,main]
    Thread[Thread-9,5,main]
    Thread[Thread-5,5,main]
    Thread[Thread-10,5,main]
    Thread[Thread-11,5,main]
    Thread[Thread-12,5,main]
    Thread[Thread-13,5,main]
    ... ...

    先说一下此处的打印,第一个参数是当前线程名称,由于线程之间是异步执行,有的还没创建好,有的后来居上就执行完了,导致打印线程的名称会这样,第二个参数是优先级,默认都是5,第三个参数是线程组名称。

    如果不停止程序,这段代码会不断创建和销毁线程,直到死机或者OOM,更尴尬的是此处的线程,还无法主动去中断。

    上述的线程启动方式在日常开发中经常看到的,但是从性能和优化的角度来说,问题还真不小。

    1. 每次都新建,性能较差(这个在线程池原理中有详细讲解)。
    2. 线程缺乏统一管理,可能无限制的创建线程,互相竞争,会带来一些不必要的麻烦。
    3. 可控性太差,比如定时定期执行,比如线程中断机制

    线程池的优点

    java提供了四大线程池,主要针对new Thread的弊端讲述优点:

    1. 降低资源消耗,不需要每次都是新建和销毁,性能得到了提高。
    2. 统一管理,可有效控制最大并发量,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
    3. 可控性很好,可以定期定时执行、线程中断机制等。

    newCachedThreadPool

    newCachedThreadPool:创建带有缓存的线程池。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * Created by Zero on 2017/8/30.
     */
    public class ThreadTest {
        public static void main(String[] args) {
            test();
        }
    
        private static void test(){
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            for (int i = 0; i < 100; i++) {
    
                cachedThreadPool.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread());
                    }
                });
            }
        }
    }
    Thread[pool-1-thread-2,5,main]
    Thread[pool-1-thread-4,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-6,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-5,5,main]
    Thread[pool-1-thread-7,5,main]
    Thread[pool-1-thread-7,5,main]
    Thread[pool-1-thread-5,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-4,5,main]
    Thread[pool-1-thread-7,5,main]
    Thread[pool-1-thread-6,5,main]
    Thread[pool-1-thread-4,5,main]
    ... ...

    由此可见,上述线程在新建的同时,还有部分线程被回收后再利用,刚刚做了下测试,在每次打印之前加了2ms的延迟,打印的都是“Thread[pool-1-thread-1,5,main]”。

    线程池为无限大(其实是Interger. MAX_VALUE),当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

    newFixedThreadPool

    newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * Created by Zero on 2017/8/30.
     */
    public class ThreadTest {
        public static void main(String[] args) {
            test();
        }
    
        private static void test(){
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
            for (int i = 0; i < 100; i++) {
    
                fixedThreadPool.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread());
                    }
                });
            }
        }
    }
    Thread[pool-1-thread-2,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-2,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-2,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-3,5,main]
    Thread[pool-1-thread-2,5,main]
    ... ...

    此处配置最大的线程长度是3,由打印可看出,此处始终在三个线程中执行。

    newScheduledThreadPool

    newScheduledThreadPool:支持定时和周期性执行的线程池。

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * Created by Zero on 2017/8/30.
     */
    public class ThreadTest {
        public static void main(String[] args) {
            test();
        }
    
        private static void test(){
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
            /**
             * 延迟3秒执行
             */
            scheduledThreadPool.schedule(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println(Thread.currentThread());
                }
            }, 3000, TimeUnit.MILLISECONDS);
    
            /**
             * 延迟1秒后每3秒执行一次
             */
            scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println(Thread.currentThread());
                }
            }, 1, 3000, TimeUnit.MILLISECONDS);
        }
    }
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-2,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-1,5,main]
    Thread[pool-1-thread-2,5,main]
    Thread[pool-1-thread-2,5,main]
    ... ...

    此方法无论任务执行时间长短,都是当第一个任务执行完成之后,延迟指定时间再开始执行第二个任务。

    在日常开发中,newScheduledThreadPool可以作为timer的替代品,对比timer,newScheduledThreadPool更安全更强大。

    newSingleThreadExecutor

    newSingleThreadExecutor:从名称上便可以看出,此线程池是一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序。如果这个线程异常结束,会有另一个取代它,保证顺序执行。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * Created by Zero on 2017/8/30.
     */
    public class ThreadTest {
        public static void main(String[] args) {
            test();
        }
    
        private static void test(){
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 100; i++) {
                singleThreadExecutor.execute(new Runnable() {
    
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread());
                    }
                });
            }
        }
    }

    得到的结果会一直是“Thread[pool-1-thread-1,5,main]”。

    总结:对于任务量少或者只有一个任务的情况下,此时使用线程池显得高射炮打蚊子了,但是对于任务量多,单个任务处理的时间比较短的情况下,你会对线程池有不一样的体会和好感。

  • 相关阅读:
    白名单执行payload之rundll32
    java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/JsonNode
    Redis key过期监听
    Kafka拦截器
    常用工具类
    Redis Pipelining
    Redis 发布订阅(Pub/Sub)
    Redis Stream
    模拟Spring自实现监听器
    Jackson
  • 原文地址:https://www.cnblogs.com/dengyungao/p/7524818.html
Copyright © 2011-2022 走看看