Вызов wait () Java-объекта нарушает синхронизацию потоков - PullRequest
5 голосов
/ 29 июня 2010
public class Main2 {
    public static void main(String[] args) {
        new Test2().start();
        new Test2().start();
    }
}

class Test2 extends Thread {
    @Override
    synchronized public void run() {
        try {
            System.out.println("begin wait");
            wait();
        } catch (Exception ex) {
        }
    }
}

Как фактический результат выполнения теста: начать ждать, начать ждать, два раза из двух нитей. Контраст с ожидаемым результатом: начать ждать, только один раз из одного из двух потоков, потому что wait () вызывается внутри синхронизированного метода run (). Зачем вызывать объектную функцию wait () для прерывания синхронизации потока?

Большое спасибо!


    public class Main3 {

    public static void main(String[] args) {
        Test3 t = new Test3();
        new Thread(t).start();
        new Thread(t).start();
    }
}

class Test3 implements Runnable {
    synchronized public void run() {
        try {
            System.out.println("begin wait");
            wait();
        } catch (Exception ex) {
        }
    }
}

@ akf & @Sean Owen

Спасибо за ваши ответы. Извините за ошибку, теперь я изменил код, чтобы поместить синхронизацию в run того же объекта (), результат остался: начать ждать, начать ждать, два раза.

@ akf

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

Не могли бы вы уточнить немного?

Ответы [ 4 ]

10 голосов
/ 29 июня 2010
  1. Объект, который вы синхронизируете в этом примере, - это не класс, а экземпляр, поэтому каждый новый объект Test2 будет синхронизироваться на другом мониторе.
  2. Метод, который вы можете здесь искать, это sleep, а не wait. wait снимет блокировку, которую synchronized захватил, и будет повторно получена после уведомления потока.

Обратите внимание, что для правильной работы теста вам необходимо заблокировать общий объект. Если вы хотите увидеть wait в действии, я собрал простое приложение, которое будет отображать фрейм с кнопкой «Уведомить». Будут запущены два потока, которые ожидают общего объекта и, в свою очередь, уведомляются при нажатии кнопки.

public static void main(String[] args)
{
    final Object lock = new Object(); 

    final JFrame frame = new JFrame("Notify Test");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JButton button = new JButton("Notify");
    button.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent evt) {
            synchronized(lock) {
                lock.notify();
            }
        }
    });
    frame.add(button);

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            frame.setVisible( true );
        }
    });

    new Thread(new Runnable() {
        public void run() {
            synchronized(lock) {
                try {
                    System.out.println("1. starting");
                    lock.wait();
                    System.out.println("1. step 1");
                    lock.wait();
                    System.out.println("1. step 2");
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
        }
    }).start();
    new Thread(new Runnable() {
        public void run() {
            synchronized(lock) {
                try {
                    System.out.println("2. starting");
                    lock.wait();
                    System.out.println("2. step 1");
                    lock.wait();
                    System.out.println("2. step 2");
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
        }
    }).start();

}

Для простого объяснения wait JavaDoc всегда является хорошим местом для начала:

Заставляет текущий поток ждать, пока другой поток не вызовет метод notify () или метод notifyAll () для этого объекта. Другими словами, этот метод ведет себя точно так же, как если бы он просто выполнял ожидание вызова (0).

Текущий поток должен иметь монитор этого объекта. Поток освобождает владельца этого монитора и ожидает, пока другой поток не уведомит потоки, ожидающие на мониторе этого объекта, чтобы он проснулся либо посредством вызова метода notify или метода notifyAll. Затем поток ожидает, пока не получит право собственности на монитор, и возобновит выполнение.

2 голосов
/ 29 июня 2010

У вас есть два разных объекта Test2.Синхронизированные методы блокировки на объекте.Они не получают одинаковую блокировку, поэтому нет, она должна печататься дважды.

1 голос
/ 29 июня 2010

простой пример, который может вам помочь:

        public class test {
            public static void main(String[] args) {
                Prova a=new Prova();
                new Test2(a).start();
                new Test2(a).start();
            }
        }
        class Prova{
            private boolean condition;

            public void f(){

                while(condition){
                    //Thread.currentThread  Returns a reference to the currently executing thread object.
                    //Thread.getName() return name Thread
                    System.out.println(Thread.currentThread().getName()+" begin wait");
                    try{
                        wait();
                    }catch(InterruptedException c){return;}
                }       

                System.out.println(Thread.currentThread().getName()+" first to take the mutex");
                condition=true;

            }
        }
        class Test2 extends Thread {
            private Prova a;
            private static boolean condition;


            public Test2(Prova a){
                this.a=a;
            }
            @Override

             public void run() {
               synchronized(a){
                try {           
                    a.f();           
                } catch (Exception ex) {
                }
               }
            }
        }

в этом случае два потока синхронизируют объект, первый получает сообщение об освобождении блокировки, второй ожидает. в этом примере используется условная переменная

0 голосов
/ 29 июня 2010

Сводка для механизма ожидания / уведомления:

1) текущий поток достигает блока синхронизированного кода одного объекта, который содержит вызов wait () , он конкурирует с другими потоками за блокировку (монитор объекта), как победитель, он выполняет блок до тех пор, пока вызовы wait ().

2) вызывая wait (), текущий поток снимает блокировку с другими конкурирующими потоками, затем останавливает выполнение, ожидает отправки уведомления от другого потока, которому удается получить блокировку.

JavaDoc:

Тема становится владельцем монитор объекта в одном из трех способы:

• Выполнение синхронизированного экземпляра метод этого объекта.

• Выполнив тело синхронизированного оператора который синхронизируется на объекте.

• Для объекты типа Class, выполняя синхронизированный статический метод этого класс.

3) другой поток достигает еще одного синхронизированного блока кода того же объекта, который содержит вызов notify / notifyAll (), конкурирует с другими потоками за блокировку, как победитель, он выполняет блок до завершения вызов для уведомления / notifyAll (). Он снимет блокировку либо по вызову wait (), либо по окончании выполнения в блоке.

4) после получения notify / notifyAll () текущий поток конкурирует за блокировку, а в качестве победителя выполнение продолжается там, где оно остановлено.

простой пример:

    public class Main3 {

    public static void main(String[] args) {
        Test3 t = new Test3();
        new Thread(t).start();
        new Thread(t).start();
        try {
            Thread.sleep(1000);
        } catch (Exception ex) {
        }
        t.testNotifyAll();
    }
}

class Test3 implements Runnable {

    synchronized public void run() {

        System.out.println(Thread.currentThread().getName() + ": " + "wait block got the lock");
        try {
            wait();
        } catch (Exception ex) {
        }
        System.out.println(Thread.currentThread().getName() + ": " + "wait block got the lock again");
        try {
            Thread.sleep(1000);
        } catch (Exception ex) {
        }
        System.out.println(Thread.currentThread().getName() + ": " + "bye wait block");

    }

    synchronized void testNotifyAll() {
        System.out.println(Thread.currentThread().getName() + ": " + "notify block got the lock");
        notifyAll();
        System.out.println(Thread.currentThread().getName() + ": " + "notify sent");
        try {
            Thread.sleep(2000);
        } catch (Exception ex) {
        }
        System.out.println(Thread.currentThread().getName() + ": " + "bye notify block");
    }
}

выход:

Thread-0 (или 1): блок ожидания получил замок

Thread-1 (или 0): получен блок ожидания замок

main: блок уведомлений получил замок

main: уведомление отправлено

основной: пока блок уведомлений

Thread-0 (или 1): блок ожидания снова получил замок

Тема-0 (или 1): пока блок ожидания

Thread-1 (или 0): блок ожидания снова получил замок

Тема-1 (или 0): пока блок ожидания

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...