1. 线程池,什么是线程池,为什么使用线程池

    线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

    为什么使用线程池:

    线程池可以给我们带来很多好处,首先通过线程池中线程的重用,减少创建和销毁线程的性能开销。其次,能控制线程池中的并发数,否则会因为大量的线程争夺CPU资源造成阻塞。最后,线程池能够对线程进行管理,比如使用ScheduledThreadPool来设置延迟N秒后执行任务,并且每隔M秒循环执行一次。

    线程的创建:

    1. new Thread(new Runnable() {
    2. @Override
    3. public void run() {
    4. //dosomething
    5. }
    6. });

    线程池图解:
    线程执行调度.png

    2、线程池的创建:

    1、使用Executors 创建

    1. //可缓存线程池 默认缓存60s,优先使用线程池已存在的线程
    2. ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    3. //创建一个重用固定数量线程的线程池
    4. ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
    5. //创建一个线程池,该线程池可以安排命令在运行后运行 给定延迟,或定期执行
    6. ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    7. //创建一个使用单个工作线程执行的执行器 ,单例
    8. ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

    2、自定义创建

    1. public ThreadPoolExecutor(int corePoolSize,
    2. int maximumPoolSize,
    3. long keepAliveTime,
    4. TimeUnit unit,
    5. BlockingQueue<Runnable> workQueue,
    6. ThreadFactory threadFactory,
    7. RejectedExecutionHandler handler)
    8. //自定义线程
    9. private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS,
    10. new LinkedBlockingQueue <>(1024),
    11. new ThreadFactoryBuilder().setNameFormat("Alan-pool-%d").build());

    关于线程池参数的含义:

    1. corePoolSize 线程池中核心线程的数量
    2. maximumPoolSize 线程池中最大线程数量
    3. keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutorallowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
    4. unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等
    5. workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutorexecute方法提交来的。
    6. threadFactory 为线程池提供创建新线程的功能,这个我们一般使用默认即可
    7. handler 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException

    线程的状态: 新建-就绪-运行-阻塞-结束

    线程运行状态.png

    1. 新建状态:
      使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
    2. 就绪状态:
      当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
    3. 运行状态:
      如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
    4. 阻塞状态:
      如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
      • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
      • 同步阻塞:线程在获取 synchronized同步锁失败(因为同步锁被其他线程占用)。
      • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
    5. 死亡状态:
      一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。