【问题标题】:wait(n) is acting different each time I change the position of synchronized keyword每次我更改同步关键字的位置时,wait(n) 的作用都不同
【发布时间】:2012-03-05 18:35:44
【问题描述】:

参考以下代码

     public void acquire(){
        synchronized(a){
            print("acquire()");
            try{
                //Thread.sleep(5000);
                synchronized(this){
                    wait(5000);
                }
                print("I have awoken");
                print("" + a);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        print("Leaving acquire()");
    }


    public void modify(int n){
        print("Entered in modfy");
        synchronized(a){
            try{
                //Thread.sleep(5000);
                synchronized(this){
                    wait(5000);
                }
                this.a=n;
                print("new value" + a);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

        final SynchoTest ttObj = new SynchoTest();
        Thread A = new Thread(new Runnable(){
            public void run() {
                ttObj.acquire();    
            }
        },"A");

        Thread B = new Thread(new Runnable(){
            public void run() {
                ttObj.modify(97);   
            }
        },"B");
        A.start();
        B.start();

据我所知,wait(n) 会暂停线程,直到调用 notify()/notifyAll() 或指定的时间 n 结束。

但是……

  • 在上述方法中,如果我像使用 Thread.sleep(n) 一样直接使用 wait(n), 我得到运行时异常。
  • 如果我同步这两种方法而不是围绕 wait(n) 同步块然后我没有得到任何异常,但两者 线程被永久阻塞。
  • 但如果我愿意,我附加了 ie wait(n) 与 synchronized 块,它工作正常。

请告诉我为什么?还告诉我,如果我使用 sleep(n) 而不是 wait(n),为什么它在定位同步块上的行为没有什么不同?

我的问题是关于等待(n)在同步关键字不同位置的各种结果。

@格雷

notify() 或 notifyAll(),且 wait() 必须在同步块中 对于你正在等待的对象

解释了为什么我通过将同步块定位在不同位置而得到运行时异常。

现在请解释一下,为什么

public void method(){
    synchronized(a){
        synchronized(this){
            wait(n);
        }
    }
}

工作正常。但是

public synchronized void method(){
    synchronized(a){
        wait(n);
    }
}

永远阻塞了我的线程。

【问题讨论】:

  • 当你说线程永远被阻塞时,你的意思是你因为没有线程启动而被卡在空白屏幕上? thread.start()
  • 您是否考虑过阅读 Javadoc 中的内容?
  • @noMAD 是的。我打印了线程状态。它说它被阻止了。

标签: java multithreading


【解决方案1】:

wait(n)sleep(n) 是完全不同的暂停代码执行的方法:

Object 实例上调用wait(n) 并将暂停执行,直到在该实例上调用notify()/notifyAll() 方法或直到计时器(参数)到期。

sleep(n)Thread 对象上被调用,就该线程而言基本上停止了世界。

你的问题归结为:

  • 您是否希望您的对象充当互斥锁,等待另一段代码完成后再继续运行?然后在其他代码中使用wait(n) 和对应的notify()/notifyAll()

  • 是否要在给定的时间范围内停止整个线程的执行?然后使用Thread.sleep(n)

【讨论】:

  • 我很清楚 sleep(n) 和 wait(n) 之间的区别。我已编辑问题以澄清我的查询。
【解决方案2】:

也许您的代码无法正常工作,因为您没有在线程上调用start()?实例化线程后,您需要:

A.start();
B.start();

此外,您不能执行以下模式。您无法在a 上同步,然后更改a 的对象。好吧,您可以做到,但我怀疑这就是您想要的。基本上a 会改变,锁定a 的其他人会锁定另一个对象,因此也可以在同步块中。非常糟糕的模式。

synchronized (a) {
    ...
    // not good
    this.a = n;
}

此外,如果您不加入线程,则主线程将继续运行,而不是等待 AB 完成。然而,JVM 将等待它们完成,因为它们不是守护线程。而且你不能保证A 会在B 之前被调用,所以修改和获取可以以任何顺序发生。

sleep(5000)wait(5000) 之间的区别在于wait 也可以通过调用notify()notifyAll() 来唤醒,并且wait() 必须在对象的synchronized 块中你正在等待。 synchronized 还会导致跨越内存屏障,从而同步多个线程之间的存储。因此它更昂贵,但在您的情况下,因为您希望共享 this.a,所以需要内存屏障。

【讨论】:

  • 我又添加了两行关于我如何启动线程的内容。虽然如果我不启动线程我不能坚持这种情况。
  • +1 建议我不应该修改我锁定的对象。您的语句 ...必须在您正在等待的对象的同步块中。 解决了我的问题。我刚刚用a.wait(n)替换了wait(n)
  • 正如你所建议的,我们不应该同步一个对象可能在同步块中被修改。在任何地方使用 synchronized(this) 也不是一个好习惯。那么我该如何决定应该同步哪个对象呢?
  • 你应该在一个常量对象上同步(通常应该是final)。定义Object lock = new Object(); 是一种常见模式。如果你有一个Set,你想synchronize,那么只要你不替换Set对象,你就应该同步它。
【解决方案3】:

定位同步关键字无关紧要。由于您锁定了其他对象并尝试等待另一个对象,因此您正面临问题。好吧,@Gray 已经解释过了,不再重复。

对于您的另一个问题,关于为什么两个线程都被阻塞;

线程 A:锁定这个 [A: Runnable]

线程 A:锁定一个 [A: Runnable]

线程 B:等待这个 [A: Runnable, B:BLOCKED]

线程 A:释放这个(遇到等待)[A: TIMED WAITING, B:BLOCKED]

线程 B: 锁定这个 [A: TIMED WAITING, B: Runnable]

线程 B:等待已被线程 A 锁定的 a [A: TIMED WAITING, B:BLOCKED]

线程 A:等待被线程 B 锁定的 this [A: BLOCKED, B:BLOCKED]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    • 2023-02-05
    • 1970-01-01
    相关资源
    最近更新 更多