Java同步字符串IllegalMonitorStateException

Java同步字符串IllegalMonitorStateException,java,multithreading,blackberry,Java,Multithreading,Blackberry,我试图让Thread2等待字符串和Thread1在字符串更新时通知,我确实同步了字符串对象,如下面的代码所示,但我仍然得到IllegalMonitorStateException这是我的代码 public class Class1{ String string = ""; public Class1(){ Thread t1 = new Thread(){ public void run(){ synchronized(stri

我试图让Thread2等待字符串和Thread1在字符串更新时通知,我确实同步了字符串对象,如下面的代码所示,但我仍然得到
IllegalMonitorStateException
这是我的代码

public class Class1{

String string   = "";

public Class1(){


    Thread t1   = new Thread(){

        public void run(){

            synchronized(string){

                string = "Something"; string.notifyAll();   //This is the line that throws an IllegalMonitorStateException
            }

        }

    };


    Thread t2   = new Thread(){

        public void run(){

            synchronized(string){

                try{

                    string.wait();

                }catch(Exception e){

                    e.printStackTrace();

                }

            }

        }

    };

    t2.start();
    t1.start();
}

}
StackTrace中除了突出显示
string.notifyAll()

  • 您的代码包含一个数据竞争,因为它访问
    synchronized
    块外部的可变
    字符串
    变量。具体来说,这发生在
    synchronized(string)
    行上。当取消引用
    字符串
    以到达监视器将被锁定的对象时,线程尚未锁定该对象。因此,您无法保证它将锁定哪个对象

  • 您对
    字符串
    变量进行了变异,这意味着它现在指向其他对象。当下一个线程获得该新对象的锁时,它将不会从任何before关系中受益,因为它是第一个获得该对象锁的线程。互斥也不能保证,因为可能有任意多个线程,每个线程都锁定一个不同的
    String
    实例,而没有争用

  • 结合上述两种现象,我们还可以看到,不能保证在
    synchronized(string)
    行上到达的对象与在synchronized块内到达的对象相同。一旦这确实是一个不同的对象,您的
    IllegalMonitorStateException
    就会随之发生

  • 总之,这种情况非常类似于根本不存在的
    synchronized

    如果您坚持使用专用的
    final
    变量引用用于锁定的对象的最佳实践,则可以避免上述所有问题。简而言之,为了修复示例中的编译错误,您必须编写以下内容:

    static String string = "";
    static final Object lock = new Object();
    
    public static void main(String[] args) {
      Thread t1 = new Thread() {
        public void run() {
          synchronized (lock) {
            ... update the string variable ...
            lock.notifyAll();
          }
        }
      };
      Thread t2 = new Thread() {
        public void run() {
          synchronized (lock) {
            try {
              lock.wait();
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }
      };
      t2.start();
      t1.start();
    }
    

    我没有收到任何
    IllegalMonitorStateException
    。当您谈论“字符串更新”时,必须假设变量
    String
    表示的实例发生了更改(当然,您提供的代码不是导致错误的代码)@Braj我想这可能是BlackBerry的问题@Marco13是的,我正在更改
    string
    @Downvoter的值。请留下评论,这个答案有什么问题?它可以改进吗?@meLove
    final
    的主要目的是阻止你更新它。这个答案有点误导。添加
    final
    并不能解决您的问题-您的问题通过不更新变量得到部分解决,而
    final
    强制执行该变量。@immibis我相信代码示例会尽可能清楚地给出这个答案。或者你能建议我如何进一步改进它吗?@WilliamF.Jameson“你的问题很可能是由于在一个非
    final
    变量上进行同步造成的”-变量是否
    final
    无关紧要;重要的是值是否更改。@immibis OK,我删除了它,但我认为这是毫无意义的,因为1)如果锁定
    final
    变量,异常肯定会消失;2) 始终锁定专用的
    最终
    变量是最佳实践,因此问题是由于未能遵循该最佳实践而产生的;3) 始终存在非最终变量可能在某个地方发生更改的风险,或此类代码可能在以后引入的风险;所以,即使你目前没有症状,如果你锁定一个非最终变量,这仍然是一个问题;4) 您可能存在非最终变量的安全发布问题。