使用 CAS 而不是同步

Use CAS instead of synchronized

最近在创建生产可重用实例的工厂时遇到一个情况

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重写这个class,即那些class java.util.concurrent.atomic 包。不幸的是,我没有想出一个合适的方法来同时使用两个原子变量,即 instancegetTimes 来安排我的代码。有人会告诉我如何在这种情况下正确使用 CAS 而不是 synchronized 而不会导致竞争条件吗?提前致谢:)

一种可能的选择是使用一个 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. The code is here 中看到类似的解决方案。在我的机器上,无锁解决方案运行速度快 3-4 倍。

如果可能想要使用两个原子,但这会使计算 getTimes 变得困难。