Спящая нить теряет право собственности на блокировку монитора?

Я хотел проверить своими глазами разницу между сном и ожиданием.

Ожидание может быть выполнено только в синхронизированном блоке, поскольку оно освобождает право собственности на блокировку монитора. В то время как сон не связан с блокировкой монитора, и поток, который уже является владельцем блокировки монитора, не должен терять свое владение, если он находится в спящем режиме.

Для этого я сделал тест:

Шаги:

  1. Запущен поток, который ожидает в синхронизированном блоке 5 секунд.
  2. Подождал 3 секунды и запустил другой поток, который получает блокировку монитора (потому что Thread-A ожидает) и просто спит в течение 5 секунд, удерживая блокировку монитора.

Ожидаемый результат: Thread-A повторно получит блокировку только через 8 секунд, когда Thread-B, наконец, освободит блокировку монитора, выйдя из блока синхронизации.

Фактический результат. Thread - 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();

        }

    }

person ZiviMagic    schedule 26.12.2012    source источник


Ответы (2)


вот гораздо лучший способ заставить тест работать и показать, что он работает. ваша проблема заключалась в том, что вы неправильно ждали и использовали 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();
      }
    }
  }
person android developer    schedule 26.12.2012

Два потока на самом деле удерживают две разные блокировки. Скажем, имя вашего класса MyClass, измените две строки synchronized (this) на synchronized (MyClass.this), чтобы два потока удерживали одну и ту же блокировку.

person TieDad    schedule 26.12.2012
comment
Ты прав. Изменил его, как вы сказали, но я не получаю исключение IllegalMonitorStateException - разве поток не должен ждать, а не генерировать исключение? - person ZiviMagic; 26.12.2012
comment
@ZiviMagic: нет, ожидание предполагает, что вы блокируете объект, для которого вызываете ожидание, но вы блокируете другой объект, поэтому возникает исключение. вместо этого позвоните Main.this.wait(). - person Nathan Hughes; 26.12.2012