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

    (一)概述

    1、没有线程池的状态:

    当我们使用一条线程的时候,先将线程对象创建出来,启动线程,在运行过程中,可能

    能完成任务,也可能会在中途被任务内容中断掉,任务还没有完成。

    即使是能够正常完成,线程对象就结束了,就变成了垃圾对象,需要被垃圾回收器回收

    如果在系统中,大量的任务都是小任务,任务消耗时间较短、线程对象的创建和消亡耗

    费的时间比较多,结果:大部分的时间都浪费在了线程对象的创建和死亡上。

    如果任务本身破坏力比较大,可能会把线程对象结束掉,就无法继续完成任务。

    2、有线程池的状态

    在没有任务的时候,先把线程对象准备好,存储到一个容器中,一旦有任务来的时候,

    就不需要创建对象,而是直接将对象获取出来执行任务

    如果任务破坏力较小,任务可以直接完成,这个线程对象不会进入死亡状态,而是被容

    器回收,继续活跃。

    如果任务破坏力较大,任务会把线程搞死,线程池会继续提供下一个线程,继续完成这

    个任务。

    (二)使用

    1、步骤:获取线程池对象;创建任务类对象;将任务类对象提交到线程池中

    2、获取线程池对象:

    工具类:Executors:生成线程池的工具类,根据需求生成指定大小的线程池

    ExecutorService Executors.newSingleThreadPool():创建一个有单个线程的线程池

    ExecutorService Executors.newFixedThreadPool(int nThreads):创建一个指定线

    程数量的线程池

    3、创建任务类对象:Runnable的实现类对象,用于定义任务内容

    4、将任务类对象提交到线程池中ExecutorService:是一个接口,不需要手动创建这个接口的实现类对象,使用方法获取

    到的就是这个接口的实现类对象,一定可以调用这个接口中的方法

    submit(Runnable r):可以将一个任务类对象,提交到线程池中,如果有空闲的线程,

    就可以马上运行这个任务,如果没有空闲线程,那么这个任务就需要等待。

    shutDown():结束线程池,已经提交的全部保证完成,不准继续提交了

    shutDownNow():结束线程池,已经开始运行的,保证完成;但是还没有运行的,已经提

    交的,不给运行了,作为返回值范围;对于没有提交的,不准提交。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Demo15_UseThreadPool {
    
        public static void main(String[] args) {
            //线程池中线程的默认名称:pool-1-thread-1
    
            /*
            * 当提交的任务数量多于池中线程数,则先给所有线程安排任务去执行,哪一个线程先执行完了自己的任务,就立马执行排队的任务
            *
            * */
            //1.获取线程池:ThreadPoolExecutor
            ExecutorService es = Executors.newFixedThreadPool(2);
    
            //2.将任务提交到线程池
            //submit(Runnable task)
            Runnable task1 = new Runnable() {
    
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("AAAAAAAAAAAAAAAAA" + Thread.currentThread().getName());
    
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
    
            Runnable task2 = new Runnable() {
    
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("@@@@@@@@@@@@@@@@@" + Thread.currentThread().getName());
    
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
    
            Runnable task3 = new Runnable() {
    
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println("5555555555555555" + Thread.currentThread().getName());
    
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
    
            es.submit(task1);
            es.submit(task2);
            es.submit(task3);
    
            //关闭线程池,执行已经提交的任务和正在排队的任务,不接受新任务
            es.shutdown();
    
            //关闭线程池,尝试结束正在执行的任务,不执行正在排队的任务,不接受新任务
            //es.shutdownNow();
    
        }
    }
    

    线程池原理

    (一)线程池部分体系

    1、Executor:接口,定义了一个接收 Runnable 对象的方法executor(Runnable

    command)

    2、ExecutorService:比 Executor 使用更广泛的子接口

    3、AbstractExecutorService:ExecutorService 抽象方法的实现类

    4、ThreadPoolExecutor:线程池,可以通过调用 Executors 工具类的静态工厂

    方法来创建线程池并返回一个 ExecutorService 的实现类对象

    (二)ThreadPoolExecutor类概述

    1、Executors工具类,是创建线程池的工具类

    2、ExecutorService接口,是工具类返回线程池对象所实现的接口

    3、ThreadPoolExecutor是ExecutorService接口的实现类,就是返回的线程池对

    象所属类

    4、ThreadPoolExecutor的构造方法:

    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
              用给定的初始参数创建新的 ThreadPoolExecutor

    ThreadPoolExecutor(

    int corePoolSize,

    int maximumPoolSize,

    long keepAliveTime,

    TimeUnit unit,

    BlockingQueue<Runnable> workQueue,

    ThreadFactory threadFactory,

    RejectedExecutionHandler handler)

    corePoolSize:

    线程池的核心线程数。在创建了线程池后,默认情况下,

    线程池中并没有任何线程,当有任务来之后,就会创建一个线程去执行任务,

    当线程池中的线程数目达到 corePoolSize 后,

    就会把到多出的任务放到队列当中

    maximumPoolSize:

    线程池允许创建的最大线程数。

    如果队列满了,并且已创建的线程数小于最大线程数,

    则线程池会再创建新的线程执行任务。

    注意:如果使用了无界的任务队列这个参数就没什么效果。

    keepAliveTime:

    线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。

    如果任务很多,并且每个任务执行的时间比较短,

    可以调大这个时间,提高线程的利用率。

    unit:

    参数 keepAliveTime 的时间单位,在TimeUnit枚举类中,有 7 种取值。

    可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),

    毫秒(MILLISECONDS),微秒(MICROSECONDS),纳秒(NANOSECONDS)

    用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS

    workQueue:

    任务队列。用于保存等待执行的任务的阻塞队列。

    可以选择有界队列和无界队列

    threadFactory:

    线程工厂,用来创建线程。主要是为了给线程起名字,

    默认工厂的线程名字:pool‐1‐thread‐1

    Executors工具类中的defaultThreadFactory()方法可以获取到

    用于创建新线程的默认线程工厂

    handler:

    拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用

    (三)阻塞队列和有界、无界队列

    1、阻塞队列:是一个支持阻塞的插入和移除方法的队列

    (1)支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的线程,直

    到队列不满

    (2)支持阻塞的移除方法:当队列为空时,获取元素的线程会等待队列变

    为非空

    2、有界队列:具有固定大小的队列,可以进队的元素个数是有限的。

    3、无界队列:是没有设置固定大小的队列。这些队列的特点是可以直接入列,

    直到内存资源耗尽

    4、相关类型即构造方法:

    (1)ArrayBlockingQueue 基于数组实现的有界阻塞队列

    (2)LinkedBlockingQueue 基于链表实现的队列,可以设定为有界,不设

    定则无界

    (3)SynchronousQueue 无界无缓冲的队列,内部容量为零,适用于元素数

    量少的场景

    (四)拒绝策略

    1、ThreadPoolExecutor.AbortPolicy

    (此项是默认策略)直接抛出异常RejectedExecutionException

    2、ThreadPoolExecutor.CallerRunsPolicy

    该任务被线程池拒绝,由调用 execute方法的线程执行该任务

    3、ThreadPoolExecutor.DiscardOldestPolicy

    抛弃队列最前面的任务,然后重新尝试执行任务

    4、ThreadPoolExecutor.DiscardPolicy

    丢弃任务,也不会抛出异常

    (五)线程池执行流程

    (六)有界队列和无界队列对线程池的影响

    1、使用有界队列:如果有新的任务需要执行,如果线程池时机线程数量小于核

    心线程数corePoolSize,则优先创建线程。如果大于核心线程数corePoolSize,

    则会将任务加入队列。如果队列已满,则在总线程数量不大于最大线程数

    maximumPoolSize的前提下,创建新的线程。如果线程数大于最大线程数

    maximumPoolSize,则执行拒绝策略

    2、使用无界队列:除了系统资源耗尽,否则无界队列不存在任务入队失败的情

    况。当有任务到来,系统的线程数小于核心线程数corePoolSize时,新建线程执

    行任务。如果后续仍然有新的任务加入,而没有空闲的线程,则任务直接入队等

    待。如果任务处理慢,而任务添加快,则队列会快速增长,直到系统资源耗尽。

    (七)ThreadPoolExecutor源码分析

  • 相关阅读:
    线程安全的单例模式
    rsync 不真正同步,只显示更新的内容
    Python_元组、字典内建方法详解
    Python_元组、字典内建方法详解
    数组求差集
    svn数据库自动发布程序
    perl 比较目录
    被驱动表 拼接列无法走索引
    FILTER NESTLOOP 中驱动表问题
    Linux_SystemLogManager
  • 原文地址:https://www.cnblogs.com/conglingkaishi/p/15128680.html
Copyright © 2011-2022 走看看