Jdk5开始出现
JUC:让开发者在多线程编程中更加简单、方便
通过JDK内置了一些类、接口、关键字,补充完善了JDK对于并发编程支持的“短板”
提供了并发编程、线程安全相关的类和接口,方便并发编程
线程创建中使用的 Callable接口 与 Future接口都属于JUC中的内容
查看JUC中的类与接口
Executor:线程池
Atomic:原子操作类
Lock:锁
Tools:信号量工具类
线程的创建和销毁,会消耗一定的系统资源。如果频繁的新建和销毁线程,可能会大量的消耗系统资源。
如何在需要频繁的创建、销毁线程的场景下保证系统响应时间更快,减少资源消耗? --- 使用线程池。
内容的一块空间。这块空间中存放了一些实例化好的线程对象。
当代码中需要使用线程时直接从线程池获取。当代码中线程执行结束或需要销毁时,把线程重新放入回到线程池,而不是让线程处于死亡状态。
默认情况下,无论需要还是不需要线程,线程池中都会有线程对象,会占用一定的内存空间。
Executor(执行器)线程池中的顶级接口,有个execute()方法,参数为Runnable接口类型
corePoolSize:核心线程数
maximumPooSize:最大线程数(核心线程数+临时线程数)
keepAliveTime:空闲时间
线程的空闲时间超过指定的时间,线程消亡,直到线程池中的个数为核心线程数
TimeUnit unit:空闲时间单位 枚举类型
纳秒、微秒、毫秒、秒、分钟、小时、天
BlockingQueue<Runnable> workQueue:阻塞队列
队列:底层实现 数组、链表;先进先出 单向
队列任务为空:阻塞获取任务
队列任务满时:阻塞添加任务
ThreadFactory threadFactory:线程工厂---创建线程对象
RejectedExecutionHandler handler:拒绝策略 ThreadPoolExecutor中的静态内部类
原子性:操作过程中不允许其他线程干扰, 可以理解为数据操作是整体,整体只有成功或失败,不允许出现部分成功部分失败。
与synchronized同步锁的原子性有些差异:多个线程操作同一资源,只能一个线程持有锁操作资源,其他线程需要等待,等待该线程执行完,其他线程再抢占资源(cpu分配)
例如:
num++在多线程下执行是不安全的, num++可以分解为
1. 读取num的值(从主内存拷贝)
2. 对num+1(在工作内存中, 操作拷贝的副本)
3. 把结果赋值给num(同步到主内存)
所以说num++是不具备原子性的。
如果希望num++具备原子性,可以把num++的三个步骤看做一个不可拆分的整体。
AtomicInteger a = new AtomicInteger();
提供的方法:
getAndIncrement(): a++,先获取值再自增
incrementAndGet(): ++a,先自增再获取值
getAndDecrement(): a--,先获取值再自减
decrementAndGet(): --a,先自减再获取值
getAndAdd(num): 先获取值,再加指定值
addAndGet(num): 先加指定值,再获取值
set(num): 修改值 a = num
getAndSet(): 先获取值,在修改为指定值
get(): 获取值
底层实现:
原子类AtomicInteger底层使用的是volatile和cas
Java.util.concurrent.locks:JUC中对锁机制支持提供的包
AQS全名AbstractQueuedSynchronizer,是并发容器JUC(java.util.concurrent)下locks包内的一个类。
它实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列。底层实现的数据结构是一个双向链表
工作原理:
AQS的核心思想为如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是使用队列实现的锁,即将暂时获取不到锁的线程加入到队列中。
AQS使用一个int state成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
AQS使用CAS对该同步状态进行原子操作实现对其值的修改,当state大于0的时候表示锁被占用,如果state等于0时表示没有占用锁。
JUC中锁的底层使用的就是AQS
Tools是JUC中的工具类, 其中包含:CountDownLatch、CyclicBarrier、Semaphore
在开发中经常遇到在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。之前是使用join() | 主线程休眠实现的,但是不够灵活,某些场合和还无法实现,所以开发了CountDownLatch这个类。底层基于AQS。
CountDown是计数递减的意思,Latch是门闩(shuan)的意思。内部维持一个递减的计数器。可以理解为初始有n个Latch,等Latch数量递减到0的时候,结束阻塞, 执行后续操作。
CountDownLatch优化了join()在解决多个线程同步时的能力,但CountDownLatch的计数器是一次性的。计数递减为0之后,再调用countDown()、await()将不起作用。为了满足计数器可以重置的目的,JDK推出了CyclicBarrier类。
await()方法表示当前线程执行时计数器值不为0则等待。如果计数器为0则继续执行。每次await()之后计算器会减少一次。当减少到0下次await从初始值重新递减。
CountDownLatch和CyclicBarrier的计数器递减的,而Semaphore的计数器是可加可减的,并可指定计数器的初始值,并且不需要事先确定同步线程的个数,等到需要同步的地方指定个数即可。且Semaphore也具有回环重置的功能,这一点和CyclicBarrier很像。底层也是基于AQS(AbstractQueuedSynchronizer)。
并发集合类:主要是提供线程安全的集合
比如:
1. ArrayList对应的并发类是CopyOnWriteArrayList
2. HashSet对应的并发类是 CopyOnWriteArraySet -- 底层数组实现
3. HashMap对应的并发类是ConcurrentHashMap
这些类的方法API和之前学习的ArrayList、HashSet、HashMap的API是相同的,所以重在实现原理上, 而不是API的使用上