zoukankan      html  css  js  c++  java
  • 线程池

    1.Java常见的四种线程池

    线程池原理:ThreadPoolExecutor里面使用到JUC同步器框架AbstractQueuedSynchronizer(俗称AQS)、大量的位操作、CAS操作。ThreadPoolExecutor提供了固定活跃线程(核心线程)、额外的线程(线程池容量 - 核心线程数这部分额外创建的线程,下面称为非核心线程)、任务队列以及拒绝策略这几个重要的功能。

    1.1 newFixedThreadPool(int poolSize)

    创建一个大小为poolSize的固定线程池,线程池是无边界阻塞队列。代码如下

    ExecutorService exec = Executors.newFixedThreadPool(poolSize);

    //它实际上是创建了一个ThreadPoolExecutor实例
    new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    //第一个参数是核心线程数;第二个参数是最大线程数;0L是长整型,代表当线程数量大于核心数时,在终止前,额外线程等待新任务的最大时间;第四个参数代表第三个参数的时间单位,这里是毫秒,第五个参数代表创建一个无边界的队列。
     

    1.2 newSingleThreadExecutor()

    创建一个单线程池,类似于newFixedThreadPool(1)
    ExecutorService exec = Executors.newSingleThreadExecutor();
    //它创建了一个被FinalizableDelegatedExecutorService修饰的ThreadPoolExecutor实例,所以它只有ThreadPoolExecutor的部分功能,且会被GC回收
    new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(11, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
     
     

    1.3 newCachedThreadPool()

    创建一个具有缓冲功能的线程池

    ExecutorService exec = Executors.newCachedThreadPool();

    //它创建了ThreadPoolExecutor实例,它是一个同步队列
    new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    //这会创建一个无界的线程池,线程可以空闲的最大时间是60秒,在这60秒都是已创建的线程都是可以复用的。
     

    1.4 newScheduledThreadPool(int poolSize)

    创建一个具有计划执行的固定大小线程池

    ExecutorService exec = Executors.newScheduledThreadPool(poolSize)

    //它创建了ThreadPoolExecutor实例,但是使用的是优先级队列
    new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue())
     

    2.ThreadPoolExecutor

    由于使用上面的线程池或多或少有些限制,所以使用底层的ThreadPoolExecutor可以自定义线程池的属性。

    2.1 参数详解

    第一个参数corePoolSize意思是核心线程大小;线程池中保留的线程数,即使是空闲状态也保留,除非设置了允许核心线程超时,默认是不设置,即不销毁核心线程

    第二个参数是最大线程数;

    第三个是keepAliveTime保留时间,当最大线程数大于核心线程数时,多余的线程保留时间,超过这个时间多余的线程将被销毁。

    第四个是第三个时间的时间单位,是属于TimeUnit的枚举值。

    第五个是一个阻塞队列,在任务提交执行前,任务都是这个阻塞队列的内容。

    第六个是线程工厂,这种工厂模式会被用于创建新线程。

    第七个是拒绝策略,当线程池线程达到最大值,阻塞队列排满时使用这个参数,,默认是抛弃执行超出的任务,直接抛异常。

    2.2 阻塞队列

    2.2.1 ArrayBlockingQueue

    基于数组的有界阻塞队列,规则是FIFO(先进先出)。

    2.2.2 LinkedBlockingQUeue

    基于链表的无界阻塞队列,规则也是FIFO,如果不设定大小,默认是无界的,此时可能会造成内存溢出等问题。

    2.2.3 SychronousQueue

    同步的阻塞队列,它不存储元素,上一个元素执行完才能执行下一个元素。

    2.3 拒绝策略

    ThreadPoolExecutor类有4个拒绝策略的内部类。默认的拒绝策略是AbortPolicy。

    2.3.1 AbortPolicy

    线程池达到最大线程数且阻塞队列满,直接抛出异常

    2.3.2 DiscardPolicy

    线程池达到最大线程数且阻塞队列满,遗弃这个任务

    2.3.3 DiscardOldestPolicy

    线程池达到最大线程数且阻塞队列满,遗弃最旧的任务,尝试将新任务添加到新线程池

    2.3.4 CallerRunPolicy

    线程池达到最大线程数且阻塞队列满,尝试使用主线程执行该任务

    2.4 线程池工作原理

    2.4.1 原理

    第一步,线程池判断核心线程池是否都在执行任务,若不是,则创建线程来执行任务;若是,则执行第二步

    第二步,使用阻塞队列来存储任务,若阻塞队列已满,则执行第三步。

    第三步,线程池判断现在是否达到最大线程数,若不是,创建线程来执行任务,若是,则执行饱和策略。

     参数如何设置:

    我们要根据任务是 计算密集型 or I/O密集型 来设置线程池的大小  看开发人员对cpu或io操作比例大小吧。计算密集型是指处理这种任务时,线程不会发生阻塞,线程不阻塞就一定程度代表 CPU 一直在忙碌;I/O 密集型 是指运行该类任务时线程多会发生阻塞,一旦阻塞,CPU 就多被闲置,浪费 CPU 资源。
    对于计算密集型的任务,在有N 个处理器的系统上,当线程池的大小为 N+1 时,能实现 CPU 的最优利用率。(即使当计算密集型的线程 偶尔由于页缺失故障或者其他原因暂停时,这个“额外” 的线程也能确保CPU 的时装周期不会被浪费。)
    对于 I/O 操作或其他 阻塞任务,由于线程并不会一直执行,因此线程池的规模应该更大 2N大小

    线程池基本原理:

    实java线程池的实现原理很简单,说白了就是一个线程集合workerSet和一个阻塞队列workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workerSet中的线程会不断的从workQueue中获取线程然后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行




     //代码  submit 和execute区别   submit 可以返回值  也可以用get 等待当前所有子线程完成。

    
    
    package cn.shenzhen.nk.Thread.pool;

    import org.apache.log4j.LogManager;
    import org.apache.log4j.Logger;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.*;

    public class InvokeAll {
    private static Logger log = LogManager.getLogger(InvokeAll.class);

    // public static int addNum = 0;
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    test();
    }

    public static void test() throws ExecutionException, InterruptedException {
    Long start = System.currentTimeMillis();
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));
    List<Future> futures = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
    Future future = threadPoolExecutor.submit(new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(10000);
    System.out.println(Thread.currentThread().getName());
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    }
    });
    futures.add(future);
    }
    for (Future future : futures
    ) {
    future.get();

    }
    System.out.println("主线程已经执行完了");


    threadPoolExecutor.shutdown();
    try {
    if (!threadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
    threadPoolExecutor.shutdownNow();
    if (!threadPoolExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
    System.out.println("线程池没有正常关闭");
    }
    }
    } catch (InterruptedException e) {
    threadPoolExecutor.shutdownNow();
    Thread.currentThread().interrupt();
    }
    //当线程池shotdown时候,线程池不再接收任何新任务,但此时线程池并不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出,调用shutdown方法后我们可以在一个死循环里面用isTerminated方法判断是否线程池中的所有线程已经执行完毕,如果子线程都结束了,我们就可以做关闭流等后续操作了
    //这种无限循环我觉得还是别用
    while (true) {
    if (threadPoolExecutor.isTerminated()) {
    System.out.println("所有的子线程都结束了!");
    Long end = System.currentTimeMillis();
    System.out.println(end - start);
    break;
    }
    }


    }

    }
     
  • 相关阅读:
    【算法•日更•第三十四期】最大流算法
    【算法•日更•第三十三期】网络流基础知识(最大流)
    【原】Java学习笔记017
    【原】Java学习笔记016
    【原】Java学习笔记015
    【原】Java学习笔记014
    【原】Java学习笔记013
    【原】Java学习笔记012
    【原】Java学习笔记011
    【原】Java学习笔记010
  • 原文地址:https://www.cnblogs.com/ningkuan/p/13864130.html
Copyright © 2011-2022 走看看