zoukankan      html  css  js  c++  java
  • 线程池(一)

    线程池:

    java给我们提供了一个线程池的工具类:Executors

    这个工具类给我们提供了几种创建线程池的方法:

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

    先看一下第一种 newFixedThreadPool,它的底层是去new一个ThreadPoolExecutor。来看看它的参数的含义:

    public ThreadPoolExecutor(int corePoolSize,                  //核心线程数
                              int maximumPoolSize,         //总线程数 = 核心线程数 + 非核心线程数
                              long keepAliveTime,          //非核心线程数的存活时间
                              TimeUnit unit,             //存活时间单位
                              BlockingQueue<Runnable> workQueue,  //工作队列
                              ThreadFactory threadFactory,        //线程工厂,一般不用
                              RejectedExecutionHandler handler)   //拒绝策略
    

     

    1.corePoolSize -> 该线程池中核心线程数最大值
    核心线程:在创建完线程池之后,核心线程先不创建,在接到任务之后创建核心线程。并且会一直存在于线程池中(即使这个线程啥都不干),有任务要执行时,如果核心线程没有被占用,会优先用核心线程执行任务。数量一般情况下设置为CPU核数的二倍即可。
    2.maximumPoolSize -> 该线程池中线程总数最大值
    线程总数=核心线程数+非核心线程数
    非核心线程:简单理解,即核心线程都被占用,但还有任务要做,就创建非核心线程
    3.keepAliveTime -> 非核心线程闲置超时时长
    这个参数可以理解为,任务少,但池中线程多,非核心线程不能白养着,超过这个时间不工作的就会被干掉,但是核心线程会保留。
    4.TimeUnit -> keepAliveTime的单位
    TimeUnit是一个枚举类型,其包括:
    NANOSECONDS : 1微毫秒 = 1微秒 / 1000
    MICROSECONDS : 1微秒 = 1毫秒 / 1000
    MILLISECONDS : 1毫秒 = 1秒 /1000
    SECONDS : 秒
    MINUTES : 分
    HOURS : 小时
    DAYS : 天
    5.BlockingQueue workQueue -> 线程池中的任务队列
    默认情况下,任务进来之后先分配给核心线程执行,核心线程如果都被占用,并不会立刻开启非核心线程执行任务,而是将任务插入任务队列等待执行,核心线程会从任务队列取任务来执行,任务队列可以设置最大值,一旦插入的任务足够多,达到最大值,才会创建非核心线程执行任务。
    workQueue有四种:
    1.SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
    2.LinkedBlockingQueue:这个队列接收到任务的时候,如果当前已经创建的核心线程数小于线程池的核心线程数上限,则新建线程(核心线程)处理任务;如果当前已经创建的核心线程数等于核心线程数上限,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
    3.ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误,或是执行实现定义好的饱和策略
    4.DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
    6.ThreadFactory threadFactory -> 创建线程的工厂
    可以用线程工厂给每个创建出来的线程设置名字。一般情况下无须设置该参数。
    7.RejectedExecutionHandler handler -> 饱和策略
    这是当任务队列和线程池都满了时所采取的应对策略,默认是AbordPolicy, 表示无法处理新任务,并抛出 RejectedExecutionException 异常。此外还有3种策略,它们分别如下。
    (1)CallerRunsPolicy:用调用者所在的线程来处理任务。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
    (2)DiscardPolicy:不能执行的任务,并将该任务删除。
    (3)DiscardOldestPolicy:丢弃队列最近的任务,并执行当前的任务。
    

      

    public class Test03 {
    
        public static void main(String[] args) {
            ExecutorService executorService1 = Executors.newFixedThreadPool(10);
    
            for (int i = 0 ; i < 100 ; i++){
                executorService1.execute(new Runnable() {
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+"  aaa");
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    
    }
    

      这段代码我们创建了一个包含10个核心线程的线程池,它的执行结果是,10个10个线程的执行,没执行10个就停止5秒钟,再执行10个线程。

    我们来看一个图

    这个线程池的核心线程数就只有10个,非核心线程数为0个,当我们来100个任务的时候,执行执行10个任务,其他的就在工作队列中排队,第二次又执行10个,以此类推,直到任务执行完毕。

    第二种线程池:Executors.newCachedThreadPool();

    public class Test03 {
    
        public static void main(String[] args) {
            ExecutorService executorService1 = Executors.newFixedThreadPool(10);
            ExecutorService executorService2 = Executors.newCachedThreadPool();
            for (int i = 0 ; i < 100 ; i++){
                executorService2.execute(new Runnable() {
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+"  aaa");
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    
    }
    

      它的底层调用跟第一个线程池的底层调用一样,但是参数不一样:

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

      可以看出,核心线程为0,非核心线程为无限大,工作队列是同步队列(也就是说,它只能处理一个任务,其余任务都是创建非核心线程来处理。)

     上图可以看出,核心线程区域为无效区域,非核心线程可以创建N个,工作队列只能处理一个任务,当100个任务到来的时候,工作队列处理1个,其余的99个创建非核心线程来处理,所以它的处理效率很快,

    但是创建的线程也很多。

    第三种:

    public class Test03 {
    
        public static void main(String[] args) {
            ExecutorService executorService1 = Executors.newFixedThreadPool(10);
            ExecutorService executorService2 = Executors.newCachedThreadPool();
            ExecutorService executorService3 = Executors.newSingleThreadExecutor();
            for (int i = 0 ; i < 100 ; i++){
                executorService3.execute(new Runnable() {
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+"  aaa");
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    
    }
    

      它的底层调用跟第一个线程池的底层调用一样,但是参数不一样:

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

      可以看出,核心线程为1,非核心线程为0个,工作队列是LinkedBlockingQueue(也就是说,所有任务进来只能排队,一个一个处理。)

     有100个任务到来,只能一个一个任务处理。其余的排队。

  • 相关阅读:
    nginx 启动相关的
    爬取豆瓣读书/文件存储数据/数据库存储数据
    python Web 开发三剑客比较
    scrapy
    爬虫自动登录抽屉
    组合搜索
    html瀑布流
    Ajax上传文件/文件预览
    Form组件
    django分页
  • 原文地址:https://www.cnblogs.com/takeyblogs/p/14132748.html
Copyright © 2011-2022 走看看