保护型暂停
代码:
// 增加超时效果
class GuardedObject {
// 结果
private Object response;
// 获取结果
public Object get() {
synchronized (this) {
while (response == null) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
// 产生结果
public void complete() {
synchronized (this) {
// 给结果成员变量赋值,赋值成功后才唤醒另一个线程
this.response = response;
this.notifyAll();
}
}
}
优势:
- 比之join,好处是可以异步执行代码
- join必须要定义全局变量,而Guarded Suspension可以用局部变量
终止模式之两阶段终止模式
在一个线程T1中,如何优雅终止线程T2?
这里的【优雅】指的是给T2正常关闭的时间。
思路:
- interrupt
- volatile
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {
// 监控线程
private Thread monitorThread;
// 停止标记(使用volatile,确保可见性,监控线程在循环的时候仍然可以看到值被修改)
private volatile boolean stop = false;
// 判断是否执行过 start 方法
private boolean starting = false;
// 启动监控线程
public void start() {
synchronized (this) {
if (starting) { // false
return;
}
starting = true;
}
monitorThread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
// 是否被打断
if (stop) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000);
log.debug("执行监控记录");
} catch (InterruptedException e) {
}
}
}, "monitor");
monitorThread.start();
}
// 停止监控线程
public void stop() {
stop = true;
monitorThread.interrupt();
}
}
单例模式
很多场景都会用到单例模式,但是多并发下的单例模式会有很多问题发生
先看看一个线程不安全的:
public final class Singleton {
private Singleton() { }
private static Singleton INSTANCE = null;
// 分析这里的线程安全, 并说明有什么缺点
public static synchronized Singleton getInstance() {
if( INSTANCE != null ){
return INSTANCE;
}
INSTANCE = new Singleton();
return INSTANCE;
}
}
- synchronize的范围太大,影响性能
- INSTANCE并没有加volatile,可能会导致指令重排
改进
public final class Singleton {
private Singleton() { }
// 加 volatile
private static volatile Singleton INSTANCE = null;
// 要点1
public static Singleton getInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
synchronized (Singleton.class) {
// 要点2:为什么还要在这里加为空判断,上面不是判断过了吗
if (INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new Singleton();
return INSTANCE;
}
}
}
- 要点1:将方法上的synchronize卸下,将锁细化
- 要点2:防止第一次初始化时,有多个线程进入,从而对上一个线程创建的单例对象进行覆盖赋值
- 这个也叫DCL懒汉式(双重监测懒汉式),缩小了锁的范围,只有第一次会进入创建对象的方法,提高了效率。
推荐方法:
public final class Singleton {
private Singleton() { }
// 问题1:属于懒汉式还是饿汉式
private static class LazyHolder {
static final Singleton INSTANCE = new Singleton();
}
// 问题2:在创建时是否有并发问题
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
- 问题一:静态内部类,只有当使用到的时候才会创建,所以是懒汉式
- 问题二:静态方法会随着静态类加载,是由JVM负责,所以没有并发问题
异步模式之工作线程
让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现 就是线程池,也体现了经典设计模式中的享元模式
但注意,当线程池中线程设置为固定大小时,会发生饥饿问题,如何解决?
- 不同的任务类型,采用不同的线程
- 创建合适的线程数量
而如何创建合适的线程数量又是一个问题,根据线程的不同任务可大体分为两种
- IO密集型
- CPU密集型