常见的单例模式中,能实现懒加载和线程安全的,一般有枚举模式、静态内部类模式、双重检查锁模式,但是枚举模式和静态内部类模式都是利用了类加载机制实现的。但是由于每次获取实例都要先获取锁,所以在性能上没有什么优势,而双重检查锁模式可以避免每次获取实例时都要加锁,性能上相对来说比较有优势。但是双重检查锁模式在创建实例的过程中,还是要对对象上锁的,是否可以无需上锁,就实现线程安全的懒加载的单例模式呢?
基于CAS的单例模式:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
public class Singleton {
private static final RunnableFuture<Singleton> buildTask = new FutureTask<Singleton>(() -> new Singleton());
private Singleton() throws InterruptedException, ExecutionException, TimeoutException {
assert buildTask.get(0, TimeUnit.NANOSECONDS) == null;
}
public static Singleton getInstance(long timeout, TimeUnit unit) throws TimeoutException {
buildTask.run();
try {
return buildTask.get(timeout, unit);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.getCause().printStackTrace();
}
return null;
}
}
下面的测试版的CAS单例模式及其测试代码:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Singleton {
private static final RunnableFuture<Singleton> buildTask = new FutureTask<Singleton>(() -> new Singleton());
private static final AtomicInteger number = new AtomicInteger();
private Singleton() throws InterruptedException, ExecutionException, TimeoutException {
Thread.sleep(100);
assert buildTask.get(0, TimeUnit.NANOSECONDS) == null;
number.addAndGet(1);
}
@Override
public String toString() {
return "Singleton 个数:" + number.get();
}
public static Singleton getInstance(long timeout, TimeUnit unit) throws TimeoutException {
buildTask.run();
try {
return buildTask.get(timeout, unit);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.getCause().printStackTrace();
}
return null;
}
}
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class SingletonTest {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
Runnable testCase = () -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(Singleton.getInstance(3, TimeUnit.SECONDS));
} catch (TimeoutException e) {
e.printStackTrace();
}
};
final int COUNT = 1000;
Thread[] testThreads = new Thread[COUNT];
for (int i = 0; i < COUNT; i++) {
testThreads[i] = new Thread(testCase);
testThreads[i].start();
}
countDownLatch.countDown();
System.out.println("************* 万箭齐发 *************");
}
}
运行结果最后一行为:
Singleton 个数:1
说明该单例模式是可以实现高并发下的。这种模式相比双重检查锁模式,由于通过CAS避免使用了Java内建锁,程序更加轻量级,同时具有超时中断的功能。但是也有一个小小的缺点,如果实例化的线程出现了异常或者被中断,后面的线程将会抛出同样的ExecutionException异常,而不是重新创建单例对象