0

我想亲眼验证睡眠和等待之间的区别。

等待只能在同步块中完成,因为它释放了监视器锁的所有权。虽然睡眠与监视器锁无关,并且已经是监视器锁所有者的线程在睡眠时不应失去其所有权。

为此我做了一个测试:

脚步:

  1. 启动了一个在同步块中等待 5 秒的线程。
  2. 等待 3 秒并启动另一个获取监视器锁的线程(因为 Thread-A 正在等待)并在持有监视器锁的同时简单地休眠 5 秒。

预期结果:线程-A 8 秒后才重新获得锁,当线程-B 最终通过退出同步块释放监视器锁。

实际结果。线程 - A 在 5 秒后获取监视器锁。

有人可以向我解释这里发生了什么吗?

public static void main(String[] args) {

    Runnable r1 = new Runnable() {

        @Override
        public void run() {

            System.out.println("r1 before synch block");
            synchronized (this) {

                System.out.println("r1 entered synch block");
                try {

                    wait(5000);
                    System.out.println("r1 finished waiting");

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    };

    Runnable r2 = new Runnable() {

        @Override
        public void run() {

            System.out.println("r2 before synch block");
            synchronized (this) {

                System.out.println("r2 entered synch block");
                try {

                    Thread.currentThread();
                    Thread.sleep(5000);
                    //wait(5000);
                    System.out.println("r2 finished waiting");

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    };

    try {

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        Thread.currentThread();
        Thread.sleep(3000);
        t2.start();

        t1.join();
        t2.join();
        System.out.println(Thread.currentThread().getName() + " Finished joining");

    } catch (Exception e) {

        e.printStackTrace();

    }

}

编辑:

好的,我理解我的错误 - 我正在等待这个 - r1/r2 而不是同一个对象。

现在我改变了它并且都在同一个对象上获取 - Main 的类实例。1. r1 获得 Main.this 的监控锁的所有权 2. r1 释放它。3. 当 r1 尝试重新获取它时,我得到一个异常:

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at Main$1.run(Main.java:28)
at java.lang.Thread.run(Unknown Source)
on synchronized (Main.this)

这里有什么问题?

public static void main(String[] args) {

        Main main = new Main();
        main.test();

    }

    public void test() {

        Runnable r1 = new Runnable() {

            @Override
            public void run() {

                System.out.println("r1 before synch block");
                synchronized (Main.this) {

                    System.out.println("r1 entered synch block");
                    try {

                        wait(5000);
                        System.out.println("r1 finished waiting");

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        };

        Runnable r2 = new Runnable() {

            @Override
            public void run() {

                System.out.println("r2 before synch block");
                synchronized (Main.this) {

                    System.out.println("r2 entered synch block");
                    try {

                        Thread.currentThread();
                        Thread.sleep(5000);
                        //wait(5000);
                        System.out.println("r2 finished waiting");

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        };

        try {

            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r2);

            t1.start();
            Thread.currentThread();
            Thread.sleep(3000);
            t2.start();

            t1.join();
            t2.join();
            System.out.println(Thread.currentThread().getName() + " Finished joining");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }
4

2 回答 2

2

这两个线程实际上持有两个不同的锁。假设你的类名是MyClass,把两行synchronized(this)改成synchronized(MyClass.this),这样两个线程就持有同一个锁。

于 2012-12-26T13:55:10.920 回答
0

这是一种更好的方法来使测试工作,并证明它有效。您的问题是您没有正确等待并且无缘无故地使用了 Thread.currentThread() 。

顺便说一句,如果您想在不丢失信号的情况下使用等待通知机制的信号,我建议您阅读此链接

public class MAIN
  {
  public static void main(final String[] args)
    {
    final Object sync =new Object();
    final long startTime=System.currentTimeMillis();
    final Runnable r1=new Runnable()
      {
        @Override
        public void run()
          {
          System.out.println((System.currentTimeMillis()-startTime)/1000+": r1 before synch block");
          synchronized(sync)
            {
            System.out.println((System.currentTimeMillis()-startTime)/1000+": r1 entered synch block");
            try
              {
              sync.wait(5000);
              System.out.println((System.currentTimeMillis()-startTime)/1000+": r1 finished waiting");
              }
            catch(final InterruptedException e)
              {
              e.printStackTrace();
              }
            }
          System.out.println((System.currentTimeMillis()-startTime)/1000+": r1 exited synch block");
          }
      };
    final Runnable r2=new Runnable()
      {
        @Override
        public void run()
          {
          System.out.println((System.currentTimeMillis()-startTime)/1000+": r2 before synch block");
          synchronized(sync)
            {
            System.out.println((System.currentTimeMillis()-startTime)/1000+": r2 entered synch block");
            try
              {
              Thread.sleep(5000);
              System.out.println((System.currentTimeMillis()-startTime)/1000+": r2 finished waiting");
              }
            catch(final InterruptedException e)
              {
              e.printStackTrace();
              }
            }
          System.out.println((System.currentTimeMillis()-startTime)/1000+": r2 exited synch block");
          }
      };
    try
      {
      final Thread t1=new Thread(r1);
      final Thread t2=new Thread(r2);
      t1.start();
      Thread.sleep(3000);
      t2.start();
      t1.join();
      t2.join();
      System.out.println((System.currentTimeMillis()-startTime)/1000+":  Finished joining");
      }
    catch(final Exception e)
      {
      e.printStackTrace();
      }
    }
  }
于 2012-12-26T18:37:12.713 回答