这是我目前所拥有的,我的方向正确吗?目的是在一个线程比其他线程更频繁地访问单例的情况下使用它,因此需要无锁代码,我想在实践中使用原子变量。
public final class ThreadSafeLazyCompareAndSwapSingleton {
private ThreadSafeLazyCompareAndSwapSingleton(){}
private static volatile ThreadSafeLazyCompareAndSwapSingleton instance;
private final static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
public static ThreadSafeLazyCompareAndSwapSingleton getCASInstance(){
if (instance==null){
boolean obs = instance==null;
while (!atomicBoolean.compareAndSet(true, obs == (instance==null))){
instance = new ThreadSafeLazyCompareAndSwapSingleton();
}
}
return instance;
}
}
嗯,这些内联条件/赋值很难阅读。这也是一个非常非标准的习语,所以我想知道你是否真的想使用它。
它至少有一个问题,它可以创建多个实例(在您的情况下可能是可以接受的,如果您想要无锁,您真的无法避免)。但我认为它也可能返回多个实例,我猜这不是你想要的。
我不确定你是否需要一个布尔值,你也可以直接在instance
字段上使用AtomicReferenceFieldUpdater。
我认为obs是不需要的,你会创建并返回一个新的实例,如果你可以设置布尔值,否则循环:
if (instance!=null)
return instance;
if (atomicBoolean.compareAndSet(false, true))
{
instance = new ThreadSafeLazyCompareAndSwapSingleton();
return instance;
}
while(instance==null);
return instance;
但是我真的不认为用这个额外的布尔值是个好主意。
一个好的方法是将线程分为两类:可以实例化类的线程和不能实例化类的线程。(为简洁起见,我将把类名缩短为Singleton
)。然后你必须考虑每一类线程需要做什么:
- 实例化线程需要存储他们在
instance
中创建的引用并返回它 - 所有其他线程需要等待,直到
instance
被设置,然后返回它
另外,我们需要确保两件事:
- 在实例化和所有返回(包括非实例化线程中的返回)之间存在happens-before边缘。
- 实例化线程的集合只有一个元素(当然,假设这两个集合都是非空的)。这是为了确保只有一个实例。
好了,这就是我们的四个要求。现在我们可以编写满足它们的代码了。
private final AtomicBoolean instantiated = new AtomicBoolean(false);
private static volatile Singleton instance = null;
// volatile ensures the happens-before edge
public static Singleton getInstance() {
// first things first, let's find out which category this thread is in
if (instantiated.compareAndSet(false, true) {
// This is the instantiating thread; the CAS ensures only one thread
// gets here. Create an instance, store it, and return it.
Singleton localInstance = new Singleton();
instance = localInstance;
return localInstance;
} else {
// Non-instantiating thread; wait for there to be an instance, and
// then return it.
Singleton localInstance = instance;
while (localInstance == null) {
localInstance = instance;
}
return localInstance;
}
}
现在,让我们确信我们的每一个条件都满足了:
- 实例化线程创建一个实例,存储它,并返回它:这是CAS的"真实"块。
- 其他线程等待实例被设置,然后返回它:这就是
while
循环的作用。 - 在实例化和返回之间有一个happens-before (HB)边:对于实例化线程,线程内语义确保了这一点。对于所有其他线程,
volatile
关键字确保写(在实例化线程中)和读(在此线程中)之间的HB边 - 实例化线程的集合恰好是一个大的,假设曾经调用过该方法:第一个命中CAS的线程将使它返回true;
我们都准备好了。
这里的一般建议是将您的需求分解成尽可能具体的子需求。然后,您可以分别处理每个问题,这更容易推理。这样做会更安全——但还有比这更好的方法。
public final class ThreadSafeLazyCompareAndSwapSingleton {
// THE instance.
private static volatile ThreadSafeLazyCompareAndSwapSingleton instance;
// Catches just one thread.
private final static AtomicBoolean trap = new AtomicBoolean(false);
public static ThreadSafeLazyCompareAndSwapSingleton getInstance() {
// All threads will spin on this loop until instance has been created.
while (instance == null) {
// ONE of the spinning threads will get past the trap. Probably the first one.
if ( trap.compareAndSet(false, true)) {
// By definition of CAS only one thread will get here and construct.
instance = new ThreadSafeLazyCompareAndSwapSingleton();
}
}
// By definition instance can never be null.
return instance;
}
// Dont let anyone but me construct.
private ThreadSafeLazyCompareAndSwapSingleton() {
}
}
请注意,如果在构造过程中抛出异常,则此操作将失败。