我上课
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()。
答案 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
是最不可能的输出,但是甚至不能完全排除该输出。而且,“不太可能”也完全基于对虚拟机通常如何运行的观察得出的假设,而这并不是可以依靠的任何东西。