使用CAS而不是同步

时间:2016-11-25 19:05:32

标签: java multithreading synchronized compare-and-swap

最近,我遇到了创建一个生产可重用实例的工厂的情况。

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包中的那些类。不幸的是,我没有想出一个正确的方法来安排我的代码同时使用两个原子变量,即instancegetTimes。有人会告诉我如何正确使用CAS代替synchronized而不会在这种情况下造成竞争条件吗?在此先感谢:)

1 个答案:

答案 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的计数很难。