为什么非阻塞算法不能正常工作

问题描述

我正在编写一个线程安全计数器。当我测试并且线程首先运行时,然后第二个一切正常。但是当线程同时进入 increment() 方法时,计数器不能正常工作。原因不清楚,我用的是原子整数。

public class CASCount {

    private final atomicreference<Integer> count = new atomicreference<>(0);
    private AtomicInteger oldValue = new AtomicInteger(0);
    private AtomicInteger newValue = new AtomicInteger(0);

    public void increment() {

        do {
            oldValue.set(count.get());
            System.out.println(oldValue + " old");

            if (oldValue.get() == -1) {
                throw new UnsupportedOperationException("Count is not impl.");
            }
            newValue.incrementAndGet();
            System.out.println(newValue + " new");
        } while (!count.compareAndSet(oldValue.get(),newValue.get()));

    }

    public int get() {
        int result = -1;
        result = count.get();
        if (result == -1) {
            throw new UnsupportedOperationException("Count is not impl.");
        }
        return result;
    }
}

 @Test
    public void whenUseCASCount() throws InterruptedException {
        CASCount count = new CASCount();
        Thread one = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("one");
                count.increment();

            }

        });

        Thread two = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("two");
                count.increment();

            }

        });
        one.start();
        two.start();
        one.join();
        two.join();
        assertthat(count.get(),is(10));
    }

解决方法

我的决定

private final AtomicReference<Integer> count = new AtomicReference<>(0);

public void increment() {
    int current,next;
    do {
        current = count.get();
        next = current + 1;
    } while (!count.compareAndSet(current,next));
}

public int get() {
    return count.get();
}
,

TL;DR - 使您的 increment 方法 synchronized


详细信息 - 尽管您使用了 atomic 变量,但这并不意味着您的类是线程安全的。这是安全的,因为您的变量的检查和增量之间可能存在(并且存在)竞争条件。

do {
    oldValue.set(count.get());
    System.out.println(oldValue + " old");

    if (oldValue.get() == -1) {
        throw new UnsupportedOperationException("Count is not impl.");
    }
    newValue.incrementAndGet();                                     <--- between here
    System.out.println(newValue + " new");
} while (!count.compareAndSet(oldValue.get(),newValue.get()));     <--- and here

check-then-act 竞争条件的典型案例。

发生这种情况是因为您的原子变量可以被多个线程访问,并且它们的共享状态可以从一个线程发生变化而在另一个线程中看不到。

为了保持状态一致性,在单个更新相关状态变量 原子操作。

- Java 并发实践

因此,当多个线程访问该方法时,我们使用内在锁(内置 synchronized)使该方法安全。发生的情况是原子变量的状态不会改变,因为每个线程一次访问一个 increment 方法。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...