• Java线程池及线程池的如何使用


    常见的四种线程池

    newFixedThreadPool

    固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。

    该线程池中的线程数量始终不变,当有新任务提交时,线程池中有空闲线程则会立即执行,如果没有,则会暂存到阻塞队列。对于固定大小的线程池,不存在线程数量的变化。同时使用无界的LinkedBlockingQueue来存放执行的任务。当任务提交十分频繁的时候,LinkedBlockingQueue

    迅速增大,存在着耗尽系统资源的问题。而且在线程池空闲时,即线程池中没有可运行任务时,它也不会释放工作线程,还会占用一定的系统资源,需要shutdown。

    newSingleThreadExecutor

    单个线程线程池,只有一个线程的线程池,阻塞队列使用的是LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,待空闲时再去执行。按照先入先出的顺序执行任务。

    newCachedThreadPool

    缓存线程池,缓存的线程默认存活60秒。线程的核心池corePoolSize大小为0,核心池最大为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。是一个直接提交的阻塞队列,    他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。

    newScheduledThreadPool

    定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。

    scheduleAtFixedRate:是以固定的频率去执行任务,周期是指每次执行任务成功执行之间的间隔。

    schedultWithFixedDelay:是以固定的延时去执行任务,延时是指上一次执行成功之后和下一次开始执行的之前的时间。

    使用实例  

    newFixedThreadPool实例:

    public class FixPoolDemo {
    
        private static Runnable getThread(final int i) {
            return new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(i);
                }
            };
        }
    
        public static void main(String args[]) {
            ExecutorService fixPool = Executors.newFixedThreadPool(5);
            for (int i = 0; i < 10; i++) {
                fixPool.execute(getThread(i));
            }
            fixPool.shutdown();
        }
    }

    newCachedThreadPool实例:

    public class CachePool {
        private static Runnable getThread(final int i){
            return new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    }catch (Exception e){
    
                    }
                    System.out.println(i);
                }
            };
        }
    
        public static  void main(String args[]){
            ExecutorService cachePool = Executors.newCachedThreadPool();
            for (int i=1;i<=10;i++){
                cachePool.execute(getThread(i));
            }
        }
    }

    这里没用调用shutDown方法,这里可以发现过60秒之后,会自动释放资源。

    newSingleThreadExecutor

    public class SingPoolDemo {
        private static Runnable getThread(final int i){
            return new Runnable() {
                @Override
                public void run() {
                    try {
    
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(i);
                }
            };
        }
    
        public static void main(String args[]) throws InterruptedException {
            ExecutorService singPool = Executors.newSingleThreadExecutor();
            for (int i=0;i<10;i++){
                singPool.execute(getThread(i));
            }
            singPool.shutdown();
        }

    这里需要注意一点,newSingleThreadExecutor和newFixedThreadPool一样,在线程池中没有任务时可执行,也不会释放系统资源的,所以需要shudown。

    newScheduledThreadPool

    public class ScheduledExecutorServiceDemo {
        public static void main(String args[]) {
    
            ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
            ses.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(4000);
                        System.out.println(Thread.currentThread().getId() + "执行了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, 0, 2, TimeUnit.SECONDS);
        }
    }

    如何选择线程池数量

    线程池的大小决定着系统的性能,过大或者过小的线程池数量都无法发挥最优的系统性能。

    当然线程池的大小也不需要做的太过于精确,只需要避免过大和过小的情况。一般来说,确定线程池的大小需要考虑CPU的数量,内存大小,任务是计算密集型还是IO密集型等因素

    NCPU = CPU的数量

    UCPU = 期望对CPU的使用率 0 ≤ UCPU ≤ 1

    W/C = 等待时间与计算时间的比率

    如果希望处理器达到理想的使用率,那么线程池的最优大小为:

    线程池大小=NCPU *UCPU(1+W/C)

    在Java中使用

    int ncpus = Runtime.getRuntime().availableProcessors();

    如何设置参数

    • 默认值
      • corePoolSize=1
      • queueCapacity=Integer.MAX_VALUE
      • maxPoolSize=Integer.MAX_VALUE
      • keepAliveTime=60s
      • allowCoreThreadTimeout=false
      • rejectedExecutionHandler=AbortPolicy()
        • 需要根据几个值来决定
          • tasks :每秒的任务数,假设为500~1000
          • taskcost:每个任务花费时间,假设为0.1s
          • responsetime:系统允许容忍的最大响应时间,假设为1s
        • 做几个计算
          • corePoolSize = 每秒需要多少个线程处理? 
            • threadcount = tasks/(1/taskcost) =tasks*taskcout =  (500~1000)*0.1 = 50~100 个线程。corePoolSize设置应该大于50
            • 根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可
          • queueCapacity = (coreSizePool/taskcost)*responsetime
            • 计算可得 queueCapacity = 80/0.1*1 = 80。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行
            • 切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。
          • maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)
            • 计算可得 maxPoolSize = (1000-80)/10 = 92
            • (最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数
          • rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理
          • keepAliveTime和allowCoreThreadTimeout采用默认通常能满足
  • 相关阅读:
    每周总结
    4月9日学习日志
    4月8日学习日志
    4月7日学习日志
    4月6日学习日志
    Cypress存取时间为10纳秒的异步SRAM
    超低功耗MCU如何降低功耗
    集成铁电存储器MCU为物联网应用提供出色性能
    读取优先和SRAM-MRAM混合结构
    磁阻式随机存储器MRAM基本原理
  • 原文地址:https://www.cnblogs.com/u013533289/p/11637506.html
Copyright © 2020-2023  润新知