最近,我遇到了创建一个生产可重用实例的工厂的情况。
public class Factory {
private static final int REUSABLE_TIMES_LIMIT = 10;
private static Product instance = new Product();
private static int getTimes;
public static synchronized Product getInstance() {
if (++getTimes >= REUSABLE_TIMES_LIMIT) {
return nextInstance();
}
return instance;
}
public static synchronized Product nextInstance() {
getTimes = 0;
instance = new Product();
return instance;
}
}
由于getInstance()
和nextInstance()
可能在我的情况下由不同的线程同时调用,我选择在每个之前添加synchronized
个关键字。但是,当许多线程进入方法时,synchronized
太重了,所以我想基于CAS
重写这个类,即java.util.concurrent.atomic
包中的那些类。不幸的是,我没有想出一个正确的方法来安排我的代码同时使用两个原子变量,即instance
和getTimes
。有人会告诉我如何正确使用CAS
代替synchronized
而不会在这种情况下造成竞争条件吗?在此先感谢:)
答案 0 :(得分:1)
一种可能的选择是使用一个AtomicReference而不是两个。无论代码的复杂程度如何,这都会使您的状态保持一致。
public static class ProductStorage {
private Product product;
private int getTimes;
public ProductStorage(Product product, int getTimes) {
this.product = product;
this.getTimes = getTimes;
}
}
public static class Factory {
private static final int REUSABLE_TIMES_LIMIT = 10;
private static AtomicReference<ProductStorage> instance = new AtomicReference<>(
new ProductStorage(new Product(), 0)
);
public static Product getInstance() {
ProductStorage current;
for(;;) {
current = instance.get();
if(current.getTimes >= REUSABLE_TIMES_LIMIT) {
instance.compareAndSet(current, new ProductStorage(new Product(), 0));
continue;
}
if(current.getTimes < REUSABLE_TIMES_LIMIT) {
if(instance.compareAndSet(current, new ProductStorage(current.product, current.getTimes + 1))) {
return current.product;
}
}
}
}
}
您可能会提到的第一件事是在这种情况下始终会分配新对象。但请记住,大多数无锁算法都是这样做的,并且它不是问题。 java中的分配速度很快,花费几纳秒。您可能还会在Martin Thompson's blog中看到类似的解决方案。代码是here。在我的机器上,无锁解决方案快速运行3-4次。
如果可能想要使用两个原子,但这将使getTimes的计数很难。