JUC

JUC
生活像一把无情刻刀!JUC:并发工具包java.util.concurrent
基本使用
lock与synchronized区别
- lock是工具包,synchronized是java关键字
- lock需要手动释放锁,synchronized由jvm管理
- lock支持悲观锁/乐观锁,取消/退出机制,synchronized是非公平锁,不支持取消/退出机制
1 | Lock 锁接口 |
TimeUnit 封装后的Thread.sleep支持各种单位,是一个枚举类,内置各种单位对象,之后可以调用sleep
线程通信
1 | Condition 用来控制线程交互 |
集合类
- CopyOnWriteArrayList 线程安全的ArrayList,读写分离,写时复制(写操作为:获取数组,复制数组长度加一,写入数据,更换原数组),适用于读多写少的场景
- CopyOnWriteHashSet 线程安全的HashSet
- ConcurrentHashMap 线程安全的HashMap,采用分段锁,写操作只锁局部的节点
辅助类
1 | CountDownLatch(减少计数):经过一定数量的计数释放await |
1 | CyclicBarrier(循环栅栏) |
1 | Semaphore(信号灯) |
CAS(Compare and Swap)
- 由硬件完成的原子操作,不加锁。
- 具有三个操作数,内存地址,预期的原值,新值。
- 使用内存地址获取值,比较与原值是否一致,一致则改为新值,否则不做操作,重试。
- CAS是cpu原语,原语的执行是连续的且不可被中断,在操作系统层面保证了一致。
1 | java.util.concurrent.atomic包中: |
缺点
- 只能保证当前对象的原子操作
- 由于每次只有一个可以成功,并发量大时cpu压力非常大
- CAS只能保证数据不被更改,但不能发现数据的ABA操作,例如将数据改成另一个值然后再改回来
Unsafe
可以直接操作特定内存的数据,类似c++的指针
Callable接口
与Runnable对比
- Callable有返回值,接口指定泛型;Runnable没有返回值
- Callable实现方法call,Runnable实现方法run
- Callable有抛异常,Runnable没有抛异常
FutureTask适配器
说明:
FutureTask实现了RunnableFutureTask接口,RunnableFuture接口继承了Runnable和Future接口,是Runnable和Callable的适配器
构造:
new FutureTask(Callable
方法:
get() 获取Callable的返回值,在返回之前阻塞线程,只计算一次,后续将缓存结果
BlockingQueue阻塞队列
说明:
当达到一定条件时阻塞队列,达到一定条件时唤醒队列,比起wait/notify我们只需要定义条件即可。
分类:
1 | ArrayBlockingQueue:由数组结构组成的有界阻塞队列 |
方法:
1 | 抛出异常 特殊值 阻塞 超时 |
ThreadPool线程池
核心参数
1 | 1.核心线程数 |
执行流程
有任务时查看是否有线程空闲,没有线程空闲查看核心线程是否达到上限,没有达到上限创建核心线程执行,达到上限时查看工作区是否装满,未装满放到工作区,装满时查看线程数量是否达到上限,没有达到上限时创建非核心线程执行当前任务(工作区满之后触发创建非核心线程的任务),达到上限执行拒绝策略。
JDK内置的拒绝策略
1 | AbortPolicy 抛出异常RejectedExecutionException |
API
ThreadPoolExecutor 线程池实现类(推荐使用,自定义参数)
构造:
new ThreadPoolExecutor(核心线程数,最大线程,非核心线程空闲超时时间,空闲超时时间单位,工作队列,线程工厂,拒绝策略)
例:new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(20),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
Executors 线程池工具类(不推荐使用)
构造:
Executors.newFixedThreadPool(4); 固定4个线程的线程池,不写默认1个线程。
Executors.newCachedThreadPool(); 不定长的线程池。
方法:
submit() 有返回值的执行,有各种重载参数
execute() 无返回值的执行,有各种重载参数
shutdown() 销毁线程池
锁降级
在写锁操作中获取读锁,保证能够第一时间读取到写入内容,并保证读取期间不被其他写入。而后进行释放写锁,读锁。
其他
对于线程数量的设置,如果是cpu密集型推荐(cpu核数+1),如果是I/O密集型(cpu核数*(1+平均等待时间/平均执行时间)) 或(cpu核数*2)

