2013-11-04 112 views
6

对不起,如果这是非常明显的或已在其他地方回答。我一直无法找到任何东西。我有以下代码:Java同步混淆

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()。现在,在迭代器方法中,我确实同步了(theLock)。现在输出更像我所期望的那样,因为锁永远不会改变。但是,现在只有线程9进入该方法。看来,线程10正在挨饿,并且永远不会回合。这似乎很奇怪。这不只是几次,它总是只是线程9迭代和睡眠。我会认为这将是9,10,9,10。或者可能是一个随机分布像9,10,10,10,9,10,9,9,10等

编辑2:我看到什么是正在发生。线程9有锁。线程10尝试进入该功能,但被立即告知要等待。函数完成后,它可能仍然是线程9转。线程9然后获得锁,并且循环继续。线程10得到一个回合的时间窗口非常小,如果它确实得到了回合,它可能会挨饿9.也就是说,在iterator()中的synchronized块之后放置yield()看起来并没有使它更多公平。我阅读了关于该方法的评论,而调度器实际上可以忽略yield()。

+3

自动装箱是魔鬼 –

+0

哈哈哈,现在看来,这可能是有问题的! :) – user2045279

回答

3

你的问题所在位置:

sharedVal++; 

那行,由于自动装箱转换成这样:

sharedVal = Integer.valueOf(sharedVal.intValue() + 1); 

它在每次运行时都会创建一个新的Integer对象,因此每次到达​​块时都会锁定一个不同的价值。

使用专用的对象同步:

private final static Object LOCK = new Object(); 

,然后更改同步使用synchronized (LOCK) {...代替。

(你也可以使用getClass()锁定,但我个人不喜欢暴露锁定对象,以公共世界)

+0

是的,谢谢你指出我的错误。请参阅我的编辑,谢谢! – user2045279

+0

当线程9从睡眠中回来时,它释放锁并重新获取它,从而使线程10不可能。它唯一的机会是调度程序在锁之间停止线程9,这是不太可能的。在迭代之间添加一个'Thread.yield()',这会让事情变得更加公平。 – Darkhogg

+0

是的,我现在看到了,并将其添加到我的第二次编辑。 Thread.yield()似乎没有多大帮助,因为调度程序可能忽略了它。话虽如此,我认为我现在了解更多。非常感谢! – user2045279

5

当你正在增加你创建一个新的整数实例和一个新的对象进行锁定。

+0

^- - - - 这。 – MadConan

+1

@OP:再次尝试使用一个可变的东西,就像一个StringBuilder(不是StringBuffer,因为StringBuffer是同步的)。 – MadConan

+0

我完全同意,并且我觉得很愚蠢。我不知道为什么Integer是不可改变的,但那是另一个问题。请参阅我的编辑。 – user2045279

1

您有效地改变该线程使用在每个迭代上的锁:++整型创建一个新的实例(Integer类是不可变的),当您执行sharedVal ++

+0

是的,另一个提到,以及指出这个错误。请参阅我的编辑 – user2045279

0

sharedVal成员varible情况正在发生变化。相反,你可以使用同步下面的语句:

同步(SympleThread.class)