JUC

发布时间 2023-08-29 20:10:17作者: yanggdgg

一、JUC

1.概念

  从Java 5开始,在JDK中多出了java.util.concurrent包(简称:JUC)。

  JUC主要是让开发者在 多线程编程 中更加简单、方便一些。

  通过JDK内置了一些类、接口、关键字,补充完善了JDK对于并发编程支持的“短板”。

2.主要包含功能

  1. Executor:线程池

  2. Atomic:原子操作类

  3. Lock:锁

  4. Tools:信号量工具类

  5. 并发集合:提供了线程安全的集合类。

二、线程池

1.为何使用线程池

对于频繁创建和销毁的线程会消耗系统大量的资源,此时通过使用线程池可以使系统响应时间更快,消耗资源更少

2.概念

线程池是内存中的一块空间,其中存放着实例化的线程对象

当需要用到线程时从线程池中取出,执行完任务或需要销毁时再放回线程池,而不是让线程处于死亡状态。

3.使用线程池的特点

3.1 优点

  1.降低系统资源的消耗。通过重用线程对象,减低由于新建和销毁线程造成的系统资源消耗

  2.提高系统响应的速度。直接从内存中获取线程,比新建线程更快

  3.提供线程的可管理性。通过对线程池中线程数量的限制,避免无限创建线程导致的内存溢出或CPU资源耗尽等问题。

3.2 缺点

  默认情况下,无论是否需要使用线程,线程池中都有一些线程对象,占用内存。

三、JUC中的线程池

1.Executor

线程池的顶级接口

该接口中只有一个execute方法,参数为Runnable类型

public interface Executor {
  void execute(Runnable command);
}

 

 

2.ThreadPoolExecutor

2.1 介绍

是JUC提供的默认的线程池的实现类

2.2 构造方法

四种有参构造

最多7个参数的构造方法

 2.3 参数

  2.3.1 corePoolSize-核心线程数

  创建线程池后,线程池中默认是没有线程的,当从线程池获取线程执行任务时才创建核心线程来执行任务。

  若没有线程数没有达到核心线程数corePoolSize,即使有空闲的核心线程,还是会创建新的核心线程来执行任务,直到达到核心线程数corePoolSize。

  线程池中的线程数达到核心线程数corePoolSize后,从线程池获取空闲的核心线程执行任务。

  2.3.2 workQueue-阻塞队列

  队列:底层是数组或链表实现的,特点是先进先出

  阻塞:队列为空时阻塞获取任务,队列满时阻塞添加任务。

  作用: 当线程池中线程数量达到核心线程数corePoolSize时,且没有空闲的核心线程,再来获取线程执行任务,任务会被添加到缓存任务的阻塞队列workQueue中;

    队列可以设置queueCapacity参数,表示最多能存储的任务数量

  2.3.3 maximumPoolSize-最大线程数

  当所有核心线程都在使用中、阻塞队列的任务也满了,线程池会创建新的线程执行任务,直到线程池中的线程数达到最大线程数maximumPoolSIze;

  线程池中线程数已达到最大线程数且都在使用中、阻塞队列也满了,线程池对于新来的任务就会执行拒绝策略

  2.3.4 keepAliveTime-线程最大空闲时间

  线程池中的空闲线程空闲的时间超过了最大空闲时间keepAliveTime就会被销毁,直到线程池中的线程数等于核心线程数;

  若设置allowCoreThreadTimeOut=true(默认false),核心线程也可以被销毁。

  2.3.5 unit-时间单位

  TimeUnit是枚举类型

public enum TimeUnit {
  //纳秒
  NANOSECONDS(TimeUnit.NANO_SCALE),
  //微妙
  MICROSECONDS(TimeUnit.MICRO_SCALE),
  //毫秒
  MILLISECONDS(TimeUnit.MILLI_SCALE),
  //
  SECONDS(TimeUnit.SECOND_SCALE),
  //分钟
  MINUTES(TimeUnit.MINUTE_SCALE),
  //小时
  HOURS(TimeUnit.HOUR_SCALE),
  //
  DAYS(TimeUnit.DAY_SCALE);
  private static final long NANO_SCALE   = 1L;
  private static final long MICRO_SCALE  = 1000L * NANO_SCALE;
  private static final long MILLI_SCALE  = 1000L * MICRO_SCALE;
  private static final long SECOND_SCALE = 1000L * MILLI_SCALE;
  private static final long MINUTE_SCALE = 60L * SECOND_SCALE;
  private static final long HOUR_SCALE   = 60L * MINUTE_SCALE;
  private static final long DAY_SCALE    = 24L * HOUR_SCALE;
}

  2.3.6 threadFactory-线程工厂

  创建线程对象

  2.3.7 handler-线程池拒绝策略

  前提:只有当任务队列已满,且线程数量已经达到maximunPoolSize才会触发拒绝策略。
  1. AbortPolicy(默认):新提交的任务将被拒绝,并抛出RejectedExecutionException异常。
  2. CallerRunsPolicy:新提交的任务将由提交该任务的线程(调用execute()方法的线程)执行。这意味着提交任务的线程将会暂时充当一个临时线程来执行任务。
  3. DiscardPolicy:新提交的任务将被丢弃,不会抛出任何异常。
  4. DiscardOldestPolicy:新提交的任务将会替换掉等待队列中最旧的任务,然后尝试再次提交该任务。

2.4 创建线程总结

  1. 当线程数小于核心线程数时,创建线程, 直到达到指定的核心线程数。

  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

  3. 当线程数大于等于核心线程数,且任务队列已满 。

​     i. 若线程数小于最大线程数,创建线程, 直到达到最大线程数 。

​     ii. 若线程数等于最大线程数,抛出异常,拒绝任务。

3. Excutors

Excutors属于一个工具类,可以快速实例化 特定类型 的线程池对象。

返回值都是ExecutorService接口的实现类,底层都调用了ThreadPoolExecutor()

 3.1 SingleThreadExecutor()

方法代码如下:

参数:

  核心线程数和最大线程数都为1

  最大空闲时间为0

  底层为链表的阻塞队列;

效果: 只会创建一个线程执行任务。

 3.2 newFixedThreadPool( int )

方法代码:

核心线程数和最大线程数都是指定的nThreads

底层为链表的阻塞队列

实际线程数量永远维持在nThreads。

 3.3 newCachedThreadPoole()

 参数:

  核心线程数:0

  最大线程数:Integer的最大值(2^31-1)

  最大空闲时间 60 秒

  底层为同步队列的阻塞队列(没有存储空间,只要有任务就必须要有线程执行,如果没有找到空闲的线程就创建新的线程执行)

效果:

  无限容量、线程空闲时间超过60秒就会被销毁、阻塞队列底层为同步队列,没有存储空间,只要有任务就必须有线程来处理,没有空闲的线程就创建新的线程来执行。

 3.4 newScheduleThreadPool()

 参数:

  核心线程数为指定的corePoolSize

  最大线程数为Integer最大值

  最大空闲时间0

  阻塞队列:DelayQueue队列

    底层使用数组实现, 初始容量为16, 超过16个任务, 扩容到之前的1.5倍

可以实现 延时执行 和 周期执行

   3.4.1 延时执行 schedule()