Многопоточность Java, заставить поток ждать другого потока или присоединиться к другому потоку - PullRequest
3 голосов
/ 07 апреля 2011

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

Хорошо, у меня есть класс с именем player, farm и land. Ферма является своего рода контроллером. У фермы есть массив объектов, называемых картой, который используется совместно с землей и игроком (предоставляется при их создании), а внутри карты находится игрок и земля.

То, что я хочу сделать, это

Я щелкнул левой кнопкой мыши на ферме и получил объект с карты в зависимости от того, где щелкнул, затем сохраняю этот объект как выбранный. Затем я щелкаю правой кнопкой мыши по другому объекту и получаю объект, давайте назовем его действующим. У Actioned есть метод getMenu (selected), который возвращает JPopMenu, основываясь на выбранном. Например, если игрок выбран и земля активирована, getMenu () вернет меню «плуг», «вода» и «движение». Если я нажму на «Плуг», то земля изменится, и, конечно же, карта также поменяется, что поделится с другим объектом. Проблема в том, что мне нужно, чтобы игрок сначала добрался до сетки около земли, прежде чем начнется настоящее действие плуга. Таким образом, в меню плуга я добавляю ActionListener следующим образом:

new ActionListener() {
        public void actionPerformed(ActionEvent e) {
                int gx = (int)Point2D.this.X();
                int gy = (int)Point2D.this.Y();
                Object lock = new Object();
                player.move(gx,gy,lock);
                try {
                    lock.wait();
                } catch (InterruptedException ex){}
                // do the real action of plow here
            }
        }
}

И способ перемещения игрока

public void move(int gx,int gy,Object lock){
    setDestination(gx, gy);
    this.lock = lock;
}

Ферма является объектом Runnable, поэтому каждый раз, когда ферма будет вызывать player.Update ().

public void update(){
    updatePosition();
    if (position==destination) {
        lock.notify();
    }
}

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

Но, конечно, это не работает должным образом, и выкидывает исключение java.lang.IllegalMonitorStateException. Я действительно не понимаю, что такое объяснение синхронизации из Интернета. Вопрос 1: Как мне использовать синхронизацию, чтобы код работал? Или что мне делать, если синхронизация здесь никогда не сработает?

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

Я делаю плеер как работающий поток. Теперь у меня есть 3 потока, ферма, слушатель (земля) и игрок.

Поэтому вместо создания объекта блокировки я создаю новый поток (Player). Метод запуска игрока:

while (position != destination) {
       try {
            Thread.sleep(100);
       } catch (InterruptedException e){}
   }

Новый метод слушателя:

        Player player = (Player) selected;
        int gx = (int)Point2D.this.X();
        int gy = (int)Point2D.this.Y();

        player.move(gx,gy);
        Thread waitplayernotmoving = new Thread(player);
        waitplayernotmoving.start();
        try {
            waitplayernotmoving.join();
        } catch (InterruptedException e){}

Это достаточно хорошо работает. Вопрос 2: перестает ли работать наземный поток во время ожидания потока игрока? если нет, как заставить поток земли остановиться, ожидая, когда поток игрока мертв?

И у меня другая проблема. Если я пашу land1, а затем, пока игрок движется, я нажимаю на land2 и пашу land2, игрок поворачивается к land2! А потом, после того как он снова не движется, обе земли распахиваются! То есть поток земли1 все еще активен.

У меня два вопроса: Вопрос 3: Можно ли заставить поток земли1 умереть, если я назначу другому игроку какое-либо действие, такое как плуг land2?

Вопрос 4: Можно ли заставить поток land2 выполняться после потока land1 на основе того, что сейчас делает моя программа? Так что действие похоже на очередь. Я пашу землю1, затем пашу землю2, действие произойдет, когда игрок переместится на землю 1, затем пашу землю 1, затем перейду на землю2 и пашу землю 2.

Вопрос 5 Помните мою карту, массив объектов, который является общим для всех? Как обеспечить его безопасность? Я имею в виду, похоже, что будет много потоков, читающих и пишущих карту. Я где-то читал, если он неизменен, это не будет проблемой. Но это переписывается так часто ... не будет ли это проблемой?

Большое спасибо за чтение этого длинного объяснения, и я надеюсь, что вы можете дать мне ответ. : (

Ответы [ 2 ]

2 голосов
/ 07 апреля 2011

Я просто отвечу на один из этих Вопросов 5:

Вы говорите: «Если это не изменится, это не будет проблемой», тогда вы говорите: «Но это так часто переписывается» - что ясобирается взять означает, что это не является неизменным.Самый простой способ - это синхронизировать весь массив.

synchronized (map) {
    // modify map
}

Недостатком этого является то, что теперь вы блокируете все остальные потоки в любое время, когда захотите изменить массив (даже если только 1 записывает и10 читаем).И есть большая вероятность, что несколько параллельных потоков на самом деле не хотят изменять один и тот же элемент, что должно быть хорошо.Единственный случай, о котором вам нужно беспокоиться, это когда несколько потоков хотят изменить один и тот же элемент карты.Второе, более детальное решение - блокировка чтения / записи.Блокируйте всю карту только тогда, когда я хочу ее изменить, и никогда не блокируйте чтение.(см. ReentrantReadWriteLock )

readLock.lock();
try {
   // read from map
} finally {
   readLock.unlock();
}

writeLock.lock();
try {
   // modify map
} finally {
   writeLock.unlock();
}

Теперь есть еще более тонкая гранулярность, когда вы блокируете только отдельные элементы на карте - но это немного выходит за рамки, я думаю.PLus дает вам возможность подумать самостоятельно.Надеюсь, это поможет.

0 голосов
/ 07 апреля 2011

Хорошо, я как-то нашел ответ, но не для всех.

Вопрос 1: Thx @noob comment: «Всякий раз, когда вы ждете и уведомляете об объекте (в данном случае блокируете), вынеобходимо выполнить эти действия в синхронизированном блоке ".Поэтому я просто делаю блокировку в блоке синхронизации.Примерно так:

       int gx = (int)Point2D.this.X();
       int gy = (int)Point2D.this.Y();
       Object lock = new Object();
       player.move(gx,gy,lock);
       synchronized(lock){
           try {
               lock.wait();
           } catch (InterruptedException ex){}
       }

Конечно, когда блокировка, дающая уведомление, также дает ему синхронизированный блок.Но сделано предупреждение: «Синхронизация на не финальном поле».Это связано с тем, что существует вероятность того, что блокировка может быть изменена другим процессом и приведет к взаимоблокировке, поскольку блокировка утратила ссылку.

Вопрос 2: Нет. Земля все еще активна.Так что с помощью ресурса.Поток все еще активен во время ожидания потока игрока, умершего.Вы можете использовать технику номер 1, заставив поток ждать.Поток, который обнаружил wait (), передаст монитор другому потоку.это своего рода сон, и он просыпается, когда объект, который вызывает wait (), вызывает notify ().

Вопрос 3: Вид нашел ответ, но все еще не попробовал его, поэтому я не буду отвечать.

Вопрос 4: Да.Это возможно.Заставьте игрока иметь очередь блокировки вместо блокировки.Поэтому в move () вместо установки блокировки добавляется блокировка в очередь.И уведомления тоже делают то же самое, это только уведомление переднего замка.После блокировки на передней панели отправьте уведомление.

Вопрос 5: Спасибо Thx to @Gandalf, но все еще не знаете, правильно ли я поступаю или нет.Не знаю, как это проверить.

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