调用同步 getter 和 setter

Invoking synchronized getter and setter

我正在尝试使用方法练习同步关键字。

我写了下面的代码:

加法器class:

public class Adder implements Runnable{
    Counter counter;
    Adder(Counter counter){
        this.counter = counter;
    }
    public void run() {
        for (int i=0; i<100; i++)
            counter.setCount(counter.getCount()+1);
    }
}

计数器class:

public class Counter {

    private int count = 0;

    public synchronized void setCount(int val){
        count = val;
    }

    public synchronized int getCount(){
        return count;
    }
}

主要内容:

public class main {

    public static void main(String[] args) {
        Counter counter = new Counter();
        Adder adder = new Adder(counter);
        Thread t1 = new Thread(adder);
        Thread t2 = new Thread(adder);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(counter.getCount());
    }
}

我希望它的输出是 200,但它不是确定性的(理论上,可以有 0-200 之间的任何值)。我怀疑问题是我正在使用 getter 和 setter 内联,即

counter.setCount(counter.getCount()+1);

出于某种原因,这个 "breaks" 我试图通过同步实现的互斥,但我不明白为什么。

我用 count++ 实现了 1 的加法,如下所示:

public synchronized void add1(){
        count++;
}

这行得通,可能是因为这样我只使用一个函数而不是两个内联函数。 您能解释一下为什么第一个实现不起作用吗?

counter.setCount(counter.getCount()+1); 不是原子的,它涉及 3 个步骤:

(1)读取count

的值

(2) count

加一

(3)写入count

的值

在第一种方法中,您是独立获取锁的,即在一个线程的 getset 调用之间会有其他线程的干扰。所以不能保证第一个线程读到的值和写的时候是一样的。

第二种方式,你是持有锁,上面3个步骤都在执行,所以你不会发现任何问题。

此外,您也可以使用线程安全AtomicInteger class.

来解决您的问题

调用 getter 和后续调用 setter 是两个独立的操作。 "Set the result of getter plus one" 在这里不是原子的。所以你可以完美地让两个get返回相同的值,并且两组相同的值增加一个。

假设 count100。你有两个线程调用 getter,都得到 100。然后它们都调用 setter,设置 101。所以计数器现在是 101,而不是 102 - 两个线程 "were there" 已经.

所以结果是不确定的,取决于来自两个线程的 get/set 操作的实际顺序。