美文网首页js css html多线程
Java线程<第五篇>:线程池

Java线程<第五篇>:线程池

作者: NoBugException | 来源:发表于2022-09-18 17:57 被阅读0次

    utils包提供开了 ExecutorService 线程池的实现,主要目的是为了重复利用线程,提高系统效率。
    Thread是一个重量级的资源,创建、启动以及销毁都是比较耗费系统资源的,因此使用线程池来管理线程是一个非常重要的编程习惯。

    1、Thread
        new Thread(new Runnable() {
            @Override
            public void run() {
    
            }
        }).start();
    

    直接使用 Thread 的弊端如下:

    • 每次new Thread新建对象性能差。
    • 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
    • 缺乏更多功能,如定时执行、定期执行、线程中断。
    2、线程池(ExecutorService、ThreadPool)
    (1)newCachedThreadPool

    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index);
                }
            });
        }
        cachedThreadPool.shutdown();
    

    线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

    (2)newFixedThreadPool

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

        ExecutorService cachedThreadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index);
                }
            });
        }
        cachedThreadPool.shutdown();
    

    这里支持的最大线程数是5, 也可以根据系统而定,获取系统可被利用的进程数

    Runtime.getRuntime().availableProcessors()
    
    (3)newScheduledThreadPool

    创建一个定长线程池,支持定时及周期性任务执行。

    定义线程池,最大线程数是5

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    

    延迟执行:

        scheduledThreadPool.schedule(new Runnable() {
    
            @Override
            public void run() {
                System.out.println("delay");
            }
        }, 3, TimeUnit.SECONDS);
    

    延迟1秒,并每隔3秒定期执行

        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
    
            @Override
            public void run() {
                System.out.println("delay 1 seconds, and excute every 3 seconds");
            }
        }, 1, 3, TimeUnit.SECONDS);
    

    关于延迟执行和周期性执行我们还会想到Timer

        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                
            }
        };
        timer.schedule(timerTask, 1000, 3000);
    
    (4)newSingleThreadExecutor

    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        System.out.println(index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    

    Java提供的四种线程池的优点

    • 重用存在的线程,减少对象创建、消亡的开销,性能佳。
    • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
    • 提供定时执行、定期执行、单线程、并发数控制等功能。
    (5)自定义线程池

    如果我们不想使用以上4种线程池,可以自定义一个线程池:

    /**
     * 线程池
     */
    public class DefaultPoolExecutor {
    
        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            @Override
            public Thread newThread(Runnable runnable) {
                // 线程
                return new Thread(runnable, "ThreadName #" + mCount.getAndIncrement());
            }
        };
    
    
        // 可用处理器的Java虚拟机的数量
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        // 最大线程数(最佳线程数 = CPU_COUNT + 1)
        private static final int MAX_CORE_POOL_SIZE = CPU_COUNT + 1;
        //空闲时间达到 30s 时,回收空闲线程(每隔30s回收一次)
        private static final long THREAD_TIMEOUT = 30L;
    
        /**
         * 新建一个线程池
         * 每个线程都会消耗大概1M的内存,使用线程池管理和复用线程
         *
         * @param corePoolSize 线程池大小
         * @return
         */
        public static ThreadPoolExecutor newDefaultPoolExecutor(int corePoolSize) {
            if (corePoolSize == 0) {
                return null;
            }
            corePoolSize = Math.min(corePoolSize, MAX_CORE_POOL_SIZE);
            int maximumPoolSize = corePoolSize;
            // corePoolSize: 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程
            // 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
            // 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
            // 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
            // 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
            // 当设置allowCoreThreadTimeOut(true)时,线程池中线程空闲时间达到keepAliveTime也将关闭
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, THREAD_TIMEOUT,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(64), sThreadFactory);
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            return threadPoolExecutor;
        }
    
    }
    

    扩充:

    除了execute可以执行线程池中的线程之外,submit也是可以的

    图片.png

    submit的效果和execute是一样的,只是execute没有返回值,而submit有返回值。

    (1)Future<?> submit(Runnable task)
            Future future1 = singleThreadExecutor.submit(new Runnable() {
                @Override
                public void run() {
    
                }
            });
    
    (2)<T> Future<T> submit(Runnable task, T result)
            Future<String> future = singleThreadExecutor.submit(new Runnable() {
                @Override
                public void run() {
    
                }
            }, "A");
    
    (3)<T> Future<T> submit(Callable<T> task);
        Callable callable = new Callable<String>() {
    
            @Override
            public String call() throws Exception {
                return "A";
            }
        };
    
        Future<String> future = singleThreadExecutor.submit(callable);
    

    如果Future中有值的话可以通过以下代码获取

            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    

    Future的其余操作如图

    图片.png

    [本章完...]

    相关文章

      网友评论

        本文标题:Java线程<第五篇>:线程池

        本文链接:https://www.haomeiwen.com/subject/rlayortx.html