java.util.concurrent
线程生命周期
死锁条件和破除
互请不循
- 死锁条件与避免:互斥(×)
- 请求与保持(一次性申请)
- 不可剥夺(主动释放)
-
sleep & wait
sleep带锁睡觉【暂停执行-自动苏醒】
-
Java创建线程的几种方式?
无返回值(Thread、Runnable)
- 返回值(Callable(Executors可以转化)、FutureTask;execute()、submit()(返回Future))
继承Thread
```java public class MyThread extends Thread { public void run() {
} }System.out.println("MyThread.run()");
MyThread t = new MyThread(); t.start();
<a name="e30dcf20"></a>
### 实现runnable接口
```java
public class RunnableTest implements Runnable {
@Override
public void run() {
System.out.println("RunnableTest.run()");
}
}
Thread t = new Thread(new RunnableTest());
t.start();
Callable/Future带返回值
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) {
CallableTest callableTest = new CallableTest() ;
// 因为Callable接口是函数式接口,可以使用Lambda表达式
FutureTask<String> task = new FutureTask<Integer>((Callable<String>)()->{
System.out.println("FutureTask and Callable");
return "hello word";
});
try{
System.out.println("子线程返回值 : " + task.get());
} catch (Exception e){
e.printStackTrace();
}
}
}
线程池创建
线程终止方式?
- 正常结束
- 使用退出标志volatile boolean退出线程
Interrupt()结束线程
- 阻塞后,调用thread.interrupt(); 中断,run中try-catch捕获InterruptedException)
- 未阻塞
public class ThreadSafe extends Thread { public void run() { while (!isInterrupted()) { // 非阻塞过程中通过判断中断标志来退出 try { Thread.sleep(5*1000);// 阻塞过程捕获中断异常来退出 } catch (InterruptedException e) { e.printStackTrace(); break;// 捕获到异常之后,执行 break 跳出循环 } } } }
t.stop()终止线程(采用 stop 是不安全的,Thread Death error/突然释放锁不可控,数据不一致)
缓存一致性问题是什么?
针对多核处理器,每个核有自己的高速缓存,共享同一主存。需要缓存一致性协议保证数据一致。指令重排
锁类型
悲观锁
- 乐观锁
- 分布式锁
- 互斥锁
- 自旋锁
- 读写锁
- 锁粗化:将多个同步块的数量减少,单个同步块的作用范围扩大(比如循环中的锁)
- 锁消除:共享数据没有竞争,就把锁去掉
- 适应性自旋锁:自旋时间不固定,虚拟机判断自旋
并发三性
原可有
volatile:可见性、部分有序性
synchronized/Lock:原子性、可见性、有序性
atomic:原子性、可见性
| 特性 | volatile | synchronized | Lock | Atomic |
|---|---|---|---|---|
| 原子性 | 无法保障 | 可以保障 | 可以保障 | 可以保障 |
| 可见性 | 可以保障 | 可以保障 | 可以保障 | 可以保障 |
| 有序性 | 一定程度保障 | 可以保障 | 可以保障 | 无法保障 |
Volatile
- 作用:保证变量的内存可见性、禁止指令重排
- 缓存一致性问题:总线加锁、缓存一致性协议
- 缓存一致性协议:MESI(写数据时,发现其他CPU有副本则将其他CPU该变量缓存行失效)store buffer
- 指令重排序(编译器、指令级、内存系统)
- as-if-serial语义:单线程保证执行结果不改变
- happens-before语义:保证正确同步的多线程程序的执行结果不被改变
- 内存屏障:汇编SFENCE/LFENCE/MFENCE,java内存屏障包括StoreStore、StoreLoad、LoadLoad、LoadStore,volatile是StoreStore+写操作+StoreLoad(SSWSL)、读操作+LoadLoad+LoadStore(RLLLS)
- 性能比synchronized好,但只能用在变量上,不会阻塞,不能保证原子性
- 缓存一致性协议MESI(缓存行、锁bus、总线风暴)
- JMM内存模型

- 可见性:嗅探机制(处理器嗅探总线),强制失效
- 有序性:禁止指令重排序(lock前缀指令 内存屏障,as-if-serial,happens-before)
-
Synchronized
Java关键字/内置锁,解决多线程之间访问资源的同步性
- 方法(实例加锁)、静态方法(Class加锁)、代码块(Class加锁)
- 作用内容同一时间只有一个线程在执行
- <1.6:重量级锁,Monitor依赖操作系统Mutex Lock(用户态和内核态切换)
- =1.6:自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁
- 四种状态:无锁、偏向锁、轻量级锁、重量级锁(一般可升不可降)
- 修饰代码块:monitorenter(对象头计数器为0可以成功获取,获取后+1)、monitorexit(锁计数设为0,锁释放)
- 修饰方法:flag:ACC_ASYNCHRONIZED
- 与ReentrantLock相比:都是可重入锁、都可以实现等待通知(notify[All]/wait; Condition)、JVM级别和API级别、RL增加了①公平非公平②等待可中断③可实现选择性通知(锁绑定多个条件)
- Monitor:EntryList、Owner、WaitSet
- 修饰:实例对象(对象头)、静态方法(ACC_SYNCHRONIZED)、代码块(monitorenter、monitorexit、count)
- 锁膨胀:无锁-偏向锁(Mark Word线程信息cas比较)-轻量锁(复制Mark Word为Lock Record,cas尝试改变指针)-自旋-重量锁(用户态内核态切换)
- 1.6之后新技术:自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁
- 双重检验锁方式实现单例模式
- 特性:有序性(as-if-serial、happens-before)、可见性(内存强制刷新)、原子性(单一线程持有)、可重入(计数)、非公平
与Lock的区别(层面、释放、中断、拿锁情况、锁住什么、Lock有读锁、公平)
CAS
乐观锁
- 伪代码:update a set value = newValue where value = {执行前查询出来的值}
ABA:版本号(值和版本号一起判断,成功则给版本号+1)-> update a set value = newValue, version = version + 1 where value = {执行前查询出来的值} and version = {执行前查询出来的版本号};时间戳
ThreadLocal
每个线程都有自己专属的本地变量
get(), set()
private static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));Thread类有threadLocals和inheritableThreadLocals变量
- 内部类:ThreadLocalMap存储以ThreadLocal为key的键值对(让线程可以关联多个ThreadLocal变量)
- key弱、value强->内存泄露,手动调用remove()
- 解决什么问题:减少临界区范围、线程切换、使用读写锁或copyonwrite机制
- 源码:ThreadLocalMap(内存泄露)
-
Atomic原子类
Integer/Long/Boolean/Reference/IntegerArray/LongArray/ReferenceArray/StampedReference(ABA)/MarkableReference/IntegerFieldUpdater/LongFieldUpdater
- incrementAndGet() get()
- CAS+volatile+native(Unsafe.objectFieldOffset())
- 包含Integer[Array]、Long[Array]、Boolean、Reference[Array]、StampedReference(解决ABA)、MarkableReference等等
线程安全原理:CAS+Volatile+Native(unsafe)
Lock类
ReentrantLock
NonfairSync(tryAcquire、acquireQueued、CAS)
- FairSync(hasQueuedPredecessors、可重入)
- 公平可选、可重入
-
ReentrantReadWriteLock
ReadLock
-
StampedLock
AQS(AbstractQueuedSynchronizer)
原理:CLH、state、独占与共享、模板方法
- 头节点设计
- 自定义AQS:重写isHeldExclusively、tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared
- CAS(实际应用、存在的问题(CPU/一个共享变量原子操作/ABA)和解决(-/AtomicReference/时间戳 标志位))
- CountDownLatch:倒计时器(A等待BCD都完成后开始工作)只有一个构造方法、只会被赋值一次、没有别的方法可以修改count
- CyclicBarrier:循环栅栏(ABCD都到达后再继续运行)
- 核心思想:被请求的共享资源(state)空闲,则将当前请求资源线程设置为有效工作线程,并将共享资源设置锁定状态。如果被请求共享资源被占用,则需要一套线程阻塞等待及被唤醒时锁分配的机制,AQS是用CHL队列实现的,即将暂时获取不到所的线程加入队列
- CHL:虚拟双向队列
- 模板方法模式:isHeldExclusively()该线程是否正在独占,用到condition才需要实现、tryAcquire(int)独占、tryRelease(int)独占、tryAcquireShared(int)共享、tryReleaseShared(int)共享
- 共享方式:exclusive独占(公平/非公平)、share共享
- ReentrantLock:独占(公平/非公平)
- Semaphore:共享
- ReentrantReadWriteLock:组合
- CountDownLatch:共享
- SynchronousQueue
-
线程池
降低资源消耗,提高响应速度,提高线程可管理性
- execute()提交不需要返回值的任务;submit()提交需要返回值的任务(Future)
- 建议不要用Executors方式创建(Fixed/Single 队列过长OOM、Cache/Schedule 线程过多OOM),ThreadPoolExecutor方式创建
- corePoolSize核心线程数/maximumPoolSize最大线程数/workQueue队列(ArrayBlockingQueue)/keepAliveTime/unit/threadFactory/handler饱和策略(抛异常、直接丢、自己线程跑、丢最早的)
- 好处:降低资源消耗、提高响应速度、提高线程可管理性
- Exercutors弊端:FixedThreadPool 和 SingleThreadExecutor允许请求的队列长度为 Integer.MAX_VALUE ,可能堆积大量的请求,从而导致OOM;CachedThreadPool 和 ScheduledThreadPool允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM
- ThreadPoolExecutor七大参数:
int corePoolSize:核心线程数
int maximumPoolSize:最大线程数
long keepAliveTime:空闲时间,等待的时间超过了keepAliveTime才会被回收销毁,allowCoreThreadTimeOut
TimeUnit unit:keepAliveTime时间单位
BlockingQueue workQueue:等待队列(LinkedBlockingQueue无界当心OOM、ArrayBlockingQueue有界/加锁/死循环阻塞队列不满时唤醒/入队、Synchronous)
ThreadFactory threadFactory:创建新线程的时候会用到
RejectedExecutionHandler handler:饱和策略(AbortPolicy:默认拒绝,直接抛出异常【直接异常】;CallerRunsPolicy:调用当前线程池的所在的线程去执行被拒绝的任务【直接执行】;DiscardPolicy:什么也不干【直接抛弃】;DiscardOldestPolicy:抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去【去旧纳新】;自定义) - 使用Has表维护线程的引用
- submit:使用Future获取任务执行结果
- 工作流程

- 运行状态:Volatile的状态码running/shutdown/stop/terminated
- largestPoolSize
- 实际应用(商品详情界面、批处理)
故障:线程池中的线程异常了会被怎么处理?
wait/notify
- 共享变量sync
- Lock同步机制
- volatile
-
进程通讯方法
管道
- 信号量
- 信号
- 消息队列
- 共享内存
-
实现生产者消费者模型
锁
- 信号量
- 线程通讯
-
优雅地手写一个线程安全的单例模式
懒汉式(多线程下数据安全问题)
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }懒汉式加锁(效率低)
public class Singleton { private Singleton singleton; private Singleton(){}; public synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }饿汉式(常用,但是有垃圾对象)
public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance() { return instance; } }双重校验锁(多线程下性能高)
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }双重校验锁+volatile
上面双重校验锁可能会出现如下线程问题:
在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象, 也就是 一个线程走到3正在初始化时被挂起,另一个线程走到0,发现instance不为null,就直接返回一个没有初始化完全的对象!
所以为了避免这种情况发生,我们给instance加上volatile关键字,利用它禁止指令重排序的功能,给写操作添加一个内存屏障。即在完全的初始化完一个对象前,不会调用读操作。
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
- 静态内部类登记
和饿汉式同样,使用ClassLoader机制来保证了初始化instance只有一个线程。而和饿汉式不一样的地方在于,使用了Lazy初始化,只有在主动调用getInstance方法时,才会实例化对象。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 枚举
采用单例模式的最佳方法,简洁、自动支持序列化机制,而且避免了线程同步问题。不过目前很少被使用。
public enum Singleton {
INSTANCE;
public void whateverMethod() {}
}
- 使用ThreadLocal实现单例模式(线程安全)
ThreadLocal为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。
public class Singleton {
private static final ThreadLocal<Singleton> tlSingleton = new ThreadLocal<Singleton>() {
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
private Singleton(){}
public static Singleton getInstance() {
return tlSingleton.get();
}
}
- 使用CAS锁实现
import java.util.concurrent.atomic.AtomicReference; public class Singleton { private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>(); private Singleton(){} public static Singleton getInstance() { for (;;) { Singleton current = INSTANCE.get(); if (current != null) { return current; } current = new Singleton(); if (INSTANCE.compareAndSet(null , current)) { return current; } } } }
