【问题标题】:Thread concurrency - synchronisation and locks.线程并发——同步和锁。
【发布时间】:2011-12-26 14:06:37
【问题描述】:
import java.util.*;
import java.io.*;
import java.util.regex.*;

class ZiggyTest2 extends Thread{

    String sa;

    public ZiggyTest2(String sa){
        this.sa = sa;
    }

    public void run(){
        synchronized(sa){
            while(!sa.equals("Done")){
                try{
                    sa.wait();
                }catch(InterruptedException is){System.out.println("IE Exception");}
            }
        }

        System.out.println(sa);
    }
}

class Test{
    private static String sa = new String("Not Done");

    public static void main(String[] args){
        Thread t1 = new ZiggyTest2(sa);
        t1.start();

        synchronized(sa){
            sa = new String("Done");
            sa.notify();
        }
    }
}

当我运行上述程序时,我得到以下异常:

Exception in thread "main" java.lang.IllegalMonitorStateException
        at java.lang.Object.notify(Native Method)
        at Test.main(ZiggyTest2.java:35)

几个问题:

  • 为什么会出现 IllegalMonitorStateException?因为 Test.sa 被分配给一个新的 String 对象,所以我期待 ZiggyTest2 线程无限期地等待,因为 sa.notify() 将在与 ZiggyTest2 中使用的锁不同的锁上调用。

  • 在上面的示例中,wait() 和 notify() 在“sa”对象上调用。单独调用 notify() 和使用对象即 sa.wait() 和 sa.notify() 调用 notify()/wait() 有什么区别?

  • 在 Test 类中,同步块是否具有 sa 对象的锁并且 sa 对象是静态的,但在 ZiggyTest2 类中,同步块使用相同的 sa 对象引用但使用非静态对象是否重要参考?假设一个是静态的,另一个不是,它们是否仍然使用同一个锁?

【问题讨论】:

    标签: java multithreading concurrency scjp ocpjp


    【解决方案1】:

    当你执行时

    sa = new String("Done");
    

    您不会通过sa 更改字符串引用的内容。您将一个新的字符串实例(一个新对象)分配给sa。字符串是不可变的。改变它们的价值是不可能的。

    这意味着您在 sa 上进行同步(第一个对象:“未完成”),然后将一个新对象分配给 sa(第二个对象:“完成”),并在第二个对象上调用 notify。由于您没有在第二个对象上同步,但在第一个对象上,您会收到 IllegalMonitorException。仅当您拥有对象的内在锁时,才允许对对象调用 notify。这就是为什么锁应该始终是最终的。

    调用 notify() 等价于调用 this.notify()。所以this.notify()sa.notify() 只是在两个不同的对象上调用notify()。第一个将通知在this 上等待的线程,第二个将通知在sa 上等待的线程。

    变量是否是静态的这一事实并不重要。锁与对象相关联,而不是与它的引用相关联。

    【讨论】:

    • 谢谢@JB。很好的解释。
    • 但是即使你在主函数的同步块中使用了一个全新的字符串引用(根本不使用sa),错误仍然存​​在!
    • 什么意思?我解释说,您必须拥有对象监视器才能在对象上调用通知。如果你调用 someObject.notify(),你肯定之前调用了 synchronized(someObject)(当然还没有释放锁)
    猜你喜欢
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-07
    相关资源
    最近更新 更多