Подтвердить что ты не робот

Java: блокирует ли блокировка ожидания() блокировки

У меня создалось впечатление, что wait() освобождает все блокировки, но я нашел это сообщение, в котором говорится

"Вызов ожидания внутри синхронизированного метода - это простой способ получить внутреннюю блокировку"

Прошу пояснить, я немного смущен.

http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

4b9b3361

Ответ 1

"Вызов ожидания внутри синхронизированного метода - это простой способ получить внутреннюю блокировку"

Это предложение ложно, это ошибка в документации.

Thread получает встроенную блокировку, когда вводит синхронизированный метод. Поток внутри синхронизированного метода устанавливается как владелец блокировки и находится в состоянии RUNNABLE. Любой поток, который пытается ввести заблокированный метод, становится BLOCKED.

Когда потоковые вызовы ждут, он освобождает текущую блокировку объекта (он держит все блокировки от других объектов), а не переходит в состояние WAITING.

Когда некоторые другие вызовы потоков уведомляют или уведомляют об этом же объекте, первый поток изменяет состояние с WAITING на BLOCKED, Уведомленная нить НЕ автоматически перезагружает замок или становится RUNNABLE, на самом деле он должен бороться за блокировку со всеми другими заблокированными потоками.

WAITING и BLOCKED состояния предотвращают запуск потока, но они очень разные.

WAITING потоки должны быть явно преобразованы в BLOCKED потоки путем уведомления из какого-то другого потока.

WAITING никогда не переходит непосредственно к RUNNABLE.

Когда поток RUNNABLE освобождает блокировку (оставляя монитор или ожидая), один из BLOCKED потоков автоматически занимает свое место.

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

public synchronized guardedJoy() {
    // must get lock before entering here
    while(!joy) {
        try {
            wait(); // releases lock here
            // must regain the lock to reentering here
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

Ответ 2

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

public class Test {
    public static void main(String[] args) throws Exception {
        testCuncurrency();
    }

    private static void testCuncurrency() throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(new WaitTester(lock));
        Thread t2 = new Thread(new WaitTester(lock));
        t1.start();
        t2.start();
        Thread.sleep(15 * 1000);
        synchronized (lock) {
            System.out.println("Time: " + new Date().toString()+ ";" + "Notifying all");
            lock.notifyAll();
        }
    }

    private static class WaitTester implements Runnable {
        private Object lock;
        public WaitTester(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            try {
                synchronized (lock) {
                    System.out.println(getTimeAndThreadName() + ":only one thread can be in synchronized block");
                    Thread.sleep(5 * 1000);

                    System.out.println(getTimeAndThreadName() + ":thread goes into waiting state and releases the lock");
                    lock.wait();

                    System.out.println(getTimeAndThreadName() + ":thread is awake and have reacquired the lock");

                    System.out.println(getTimeAndThreadName() + ":syncronized block have finished");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private static String getTimeAndThreadName() {
        return "Time: " + new Date().toString() + ";" + Thread.currentThread().getName();
    }
}

Запуск этого класса на моем компьютере возвращает следующий результат:

Time: Tue Mar 29 09:16:37 EEST 2016;Thread-0:only one thread can be in synchronized block
Time: Tue Mar 29 09:16:42 EEST 2016;Thread-0:thread goes into waiting state and releases the lock
Time: Tue Mar 29 09:16:42 EEST 2016;Thread-1:only one thread can be in synchronized block
Time: Tue Mar 29 09:16:47 EEST 2016;Thread-1:thread goes into waiting state and releases the lock
Time: Tue Mar 29 09:16:52 EEST 2016;Notifying all
Time: Tue Mar 29 09:16:52 EEST 2016;Thread-1:thread is awake and have reacquired the lock
Time: Tue Mar 29 09:16:57 EEST 2016;Thread-1:syncronized block have finished
Time: Tue Mar 29 09:16:57 EEST 2016;Thread-0:thread is awake and have reacquired the lock
Time: Tue Mar 29 09:17:02 EEST 2016;Thread-0:syncronized block have finished

Ответ 3

Я думаю, что это утверждение должно быть видно в его полном контексте.

Когда поток вызывает d.wait, он должен иметь встроенную блокировку для d - в противном случае возникает ошибка. Вызов ожидания внутри синхронизированного метод - простой способ получить встроенный замок.

Я понимаю, что они должны упростить это:

Вызов методов synchronized позволяет блокировать объект, мы можем просто поместить вызов wait() внутри метода synchronized.

Ответ 4

wait:: является частью класса java.lang.Object, поэтому мы можем вызвать этот метод только для объекта. вызов этого требует контроля (блокировки) на этом объекте, иначе IllegalMonitorStateException будет выброшено, например) Thread.currentThread(). Wait() выкинет это исключение под кодом ниже.

   Example1
   public void doSomething() {                                          Line 1
        synchronized(lockObject) { //lock acquired                      Line 2
            lockObject.wait();     // NOT Thread.currentThread().wait() Line 3
        }
    }

Теперь ожидание вызова в строке 3 освободит блокировку, полученную в строке 2. Таким образом, любой другой поток, введенный в строку 1 и ожидающий получения блокировки на lockObject, получит эту блокировку и продолжит.

Теперь рассмотрим это Example2; здесь освобождается блокировка lockObject2, а текущий поток удерживает lockObject1 lock. Это приведет к тупику; Поэтому пользователь должен быть более осторожным в этом случае.

   Example2 
        public void doSomething() {                                     Line 1
             synchronized(lockObject1) { //lock1 acquired               Line 2
                 synchronized(lockObject2) { //lock2 acquired           Line 3
                     lockObject2.wait();                                Line 4
                 }
             }
        }

Если это ожидание заменяется на sleep, yield, or join, они не имеют чтобы освободить замок. Только подождите, может освободить блокировку, которую он держит.

Просто осторожно на t1.sleep()/t1.yield() где статические api и всегда действие будет выполняться на currentThread не на потоке t1.

Тогда давайте понять, в чем разница между suspend и этими api sleep, yield, join; потому что suspend устарел, чтобы избежать ситуации в потоке, удерживая блокировку, которая приведет к тупиковой ситуации, когда она приостановлена ​​(не работает) для undefined времени. Это то же поведение для другого apis.

Ответ будет приостановлен/возобновлен будет выполняться на других потоках, например t1.suspend(), где, поскольку эти api приостанавливают Thread.currentThread().     Следовательно, пользователь должен проявлять осторожность в том, чтобы заботиться о том, чтобы не держать блокировки перед вызовом этих api, чтобы избежать тупика. Это не при вызове suspend. Поток звонков не знает о потоке звонящего (блокировка) состояние, на котором он будет выполнять приостановку, поэтому устарел.