微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决为什么非阻塞算法不能正常工作

我正在编写一个线程安全计数器。当我测试并且线程首先运行时,然后第二个一切正常。但是当线程同时进入 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 方法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?