Как использовать синхронизированный в Java - PullRequest
0 голосов
/ 12 мая 2018

Надеюсь, я смогу понятно описать ситуацию. Я хочу запустить некоторое количество потоков, и все они будут выполнять один синхронизированный метод. Предположим, что первый поток проверяет значение переменной в этом методе, после чего после проверки будет снята блокировка. Затем второй поток вызывает ту же функцию. Но первый поток затем (через несколько мс) изменит эту переменную, которая находится в другом классе, но второй поток (возможно) проверит переменную до того, как первый изменил ее. Как я могу заставить второй поток ждать (без сна), пока первый не закончил и изменил переменную, прежде чем второй проверяет значение? Может ли первый послать сигнал типа «переменная изменена, вы можете проверить это сейчас»?

Теперь я пытаюсь записать это в коде: все потоки запущены, все это выполняется:

abstract class Animal {
protected House house;
    abstract boolean eating();
    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                if(eating()) {
                   goEat();//here house.eatingRoom.count will be changed
                   Thread.sleep(1000);
                    goback();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Все они используют этот метод:

class Cat extends Animal {

@Override
   synchronized boolean eating() {
       if (house.eatingRoom.count == 0) 
           return true;//first thread release lock and 2 thread access it but the value is not changed yet
       else
           return false;
    }
}

И

class EatingRoom {
    final Set<Animal> count = new HashSet<>();
    synchronized void add(Cat c) {
         count.add(c);
    }
}

для завершения:

public class House extends Thread {

    final EatingRoom eatingRoom = new EatingRoom();
//start all threads here so run in Animal class is executed..

}

1 Ответ

0 голосов
/ 12 мая 2018

Проблема, которую вы описываете, звучит так, как если бы вы могли извлечь выгоду из примитивов синхронизации Java, таких как Object.wait и Object.notify.

Поток, которому принадлежит блокировка / монитор данного объекта (например, с помощью ключевого слова synchronized), может вызвать wait вместо того, чтобы зацикливаться и спать в шаблоне занятости / ожидания, как в while(!Thread.interrupted()), который может тратить много циклов ЦП.

Как только поток переходит в состояние ожидания, он снимает блокировку, которую он удерживает, что позволяет другому потоку получить такую ​​же блокировку и, возможно, изменить какое-то состояние, а затем уведомить один или несколько ожидающих потоков через notify / notifyAll.

Обратите внимание, что необходимо соблюдать осторожность, чтобы обеспечить получение и снятие блокировок в одном и том же порядке, чтобы избежать сценариев тупиковой ситуации, когда задействовано более одной блокировки. Также попробуйте использовать тайм-ауты во время ожидания, чтобы убедиться, что ваш поток не будет ждать неопределенно долго условия, которое может никогда не возникнуть. Если при вызове notify много ожидающих потоков, имейте в виду, что вы можете не знать, какой поток будет запланирован, но вы можете установить политику справедливости, чтобы помочь повлиять на это.

В зависимости от структуры вашего кода вы можете избежать некоторых примитивов более низкого уровня, таких как синхронизированные блоки, используя некоторые API более высокого уровня, такие как https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html или ключевые слова, такие как volatile, для переменных, которые содержат общее изменяемое состояние ( как условие, которое вы хотите дождаться, чтобы убедиться, что результат записи наблюдается при последующем чтении в отношении «происходит до».

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