public class SimpleThread extends Thread {
public static Integer sharedVal = 0;
public SimpleThread() {
}
@Override
public void run() {
while(true) {
iterator();
}
}
public void theSleeper() {
System.out.println("Thread: " + this.getId() + " is going to sleep!");
try {
this.sleep(5000);
} catch(Exception e) {
}
}
public void iterator() {
synchronized(sharedVal) {
System.out.println("Iterating sharedVal in thread: " + this.getId());
sharedVal++;
System.out.println(sharedVal);
theSleeper();
System.out.println("Thread : " + getId() + " is done sleeping, trying to iterate again...");
}
}
}
我创建了这个SimpleThread类的两个实例,并执行run方法。我希望看到类似的东西:线程9递增...线程9睡觉...(5秒后)线程10递增....线程10睡觉....我希望这是因为我锁定迭代器方法,以便一次只能有一个线程输入它。相反,两个线程都会递增,然后都会等待5秒。这永远重复。我在这里想念的是什么,以至于我会得到预期的行为?非常感谢!
编辑:我创建了一个新的公共静态变量:public static Object theLock = new Object()。现在,在迭代器方法中,我做了synchronized(theLock)。输出现在更像我的预期,因为锁永远不会改变。但是,现在只有线程9进入该方法。似乎线程10正在挨饿,并且永远不会转弯。这看起来很奇怪。这不仅仅是几次,它总是只是线程9迭代和睡眠。我认为这将是9,10,9,10。或者可能是随机分布,如9,10,10,10,9,10,9,9,10等。
EDIT2:我看到现在发生了什么。线程9具有锁定。线程10尝试进入该功能,但是立刻被告知等待。函数完成后,它可能仍然是线程9转。线程9然后重新获得锁定,并且循环继续。线程10获得转弯的时间窗口非常小,如果它确实转了,它可能会饿死9.这就是说,在iterator()中的synchronized块之后放置yield()似乎没有让它更多公平。我阅读了关于该方法的注释,并且调度程序实际上可以忽略yield()。
答案 0 :(得分:5)
当您进行增量时,您将创建一个新的整数实例和一个用于锁定的新对象。
答案 1 :(得分:3)
你的问题就在这里:
sharedVal++;
由于自动装箱而导致的这一行转化为:
sharedVal = Integer.valueOf(sharedVal.intValue() + 1);
每次运行时都会创建一个新的Integer
对象,因此每次访问synchronized
块时,它都会锁定不同的值。
使用专用对象进行同步:
private final static Object LOCK = new Object();
然后更改同步以改为使用synchronized (LOCK) {...
。
(您也可以使用getClass()
锁定,但我个人不喜欢向公共世界公开锁定对象)
答案 2 :(得分:1)
您正在有效地更改线程在每次迭代时使用的锁定:整数上的++会创建一个新实例(整数类是不可变的)
答案 3 :(得分:0)
当您执行sharedVal ++时,sharedVal成员变量实例正在更改。相反,您可以使用以下语句进行同步:
同步(SympleThread.class)