Java SynchronizedCounter无法按预期运行

时间:2018-11-10 12:26:27

标签: java multithreading synchronization

我上课

class SynchronizedCounter {
    private int i = 0;

    public synchronized void increment() {
        i++;
    }

    public synchronized void decrement() {
        i--;
    }

    public synchronized int getValue() {
        return i;
    }
}

这样使用:

public class CounterTest {
    public static void main(String[] args) throws InterruptedException {
        SynchronizedCounter c = new SynchronizedCounter();
        Thread d = new Thread(new D(c));
        Thread e = new Thread(new E(c));
        d.start();
        e.start();
        System.out.println(c.getValue());           
    }
}

其中D类的实现如下:

class D implements Runnable {
    private SynchronizedCounter counter;

    D(SynchronizedCounter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        counter.increment();
    }
}

与D类相比,E类只有一条不同的线

 counter.decrement();

在运行方法中。

我希望总是打印0,因为SynchronizedCounter类的方法都已同步,但是有时我得到1。 您能否解释一下这段代码有什么问题? 当我在synced(c)块中运行d.start()和e.start()时,它可以按预期工作,当我在d.start()之后添加d.join()并在之后添加e.join()时,也会发生同样的情况e.start()。

1 个答案:

答案 0 :(得分:2)

您对线程不正确的假设很多。 您的两个不成立的主要假设是:

  • 所有线程以相同的速度运行。
  • 所有线程立即启动。

执行顺序是随机的。 线程同时运行。 在下文中,序列是指线程通过方法调用命中SynchronizedCounter的序列。因为synchronized就是这样做的,所以它通过使用监视器来保护并发线程以顺序访问(如果您不知道监视器是什么,请改为读取互斥量信号灯,这是不同的,但是对于本说明而言,区别是不重要)。 您可以期望以下三个输出中的任何一个:

  • 0(如果序列为D,E,main;或E,D,主要;或主D,E;或主要的E,D。
  • 1(如果序列为D,main,E
  • -1,以防序列为E,main,D。

线程不会立即启动

调用new Thread().start()时,Thread变为可运行。但是,由调度程序决定线程何时实际获得CPU时间。而且这取决于程序影响范围之外的因素,有时甚至取决于VM影响范围之外的因素。例如,由于耗尽其时间片而要抢占现有线程的距离有多近,或者当前是否有空闲的CPU内核。

线程以不同的速度运行

线程速度由程序影响范围之外的各种因素决定。例如,如果线程想要访问数据,则仅举一个示例,无论该数据是否被缓存以及在哪个缓存中。

输出-1是最不可能的输出,但是甚至不能完全排除该输出。而且,“不太可能”也完全基于对虚拟机通常如何运行的观察得出的假设,而这并不是可以依靠的任何东西。