单实例的并发控制,主要是针对JVM内,我们常规的手段即可满足需求,常见的手段大概有下面这些
- 同步代码块
- CAS自旋
- 锁
- 阻塞队列,令牌桶等
1.1 同步代码块
通过同步代码块,来确保同一时刻只会有一个线程执行对应的业务逻辑,常见的使用姿势如下
public synchronized doProcess() {
// 同步代码块,只会有一个线程执行
}
复制代码
一般推荐使用最小区间使用原则,尽量不要直接在方法上加synchronized
,比如经典的双重判定单例模式
public class Single {
private static volatile Single instance;
private Single() {}
public static Single getInstance() {
if (instance == null) {
synchronized(Single.class) {
if (instance == null) instance = new Single();
}
}
return instance;
}
}
复制代码
1.2 CAS自旋方式
比如AtomicXXX
原子类中的很多实现,就是借助unsafe的CAS来实现的,如下
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
// unsafe 实现
// cas + 自选,不断的尝试更新设置,直到成功为止
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
复制代码
1.3 锁
jdk本身提供了不少的锁,为了实现单实例的并发控制,我们需要选择写锁;如果支持多读,单实例写,则可以考虑读写锁;一般使用姿势也比较简单
private void doSome(ReentrantReadWriteLock.WriteLock writeLock) {
try {
writeLock.lock();
System.out.println("持有锁成功 " + Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("执行完毕! " + Thread.currentThread().getName());
writeLock.unlock();
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void lock() throws InterruptedException {
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
new Thread(()->doSome(re