Можно ли дождаться завершения методов, отсутствующих в потоках, в Java? - PullRequest
1 голос
/ 15 марта 2009

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

Вот что я сделал, в основном:

synchronized public void compute()
{
    other.mark(variable);
    try
    {
        wait();
    }
    catch(InterruptedException e)
    {
    }
}

в OtherClass, у меня есть

synchronized public void mark(int var)
{
    //change some stuff
    repaint();
    notify();
}

Что происходит, так это то, что compute () ждет вечно. Я думал, что это сработает, так как компилятор не дал никаких ошибок. Ни один из классов не реализует Runnable и не расширяет Thread, так что, может быть, в этом проблема? Я не уверен, так как думаю, что меня предупредят, если эти объекты не смогут использовать такие методы.

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

Ответы [ 7 ]

2 голосов
/ 16 марта 2009

Ваш вопрос говорит о том, что вы хотите выполнить какую-либо операцию, которая обновляет состояние графического интерфейса либо по его завершении, либо уведомляет графический интерфейс о своем прогрессе. Для этого и был разработан SwingWorker . Есть несколько примеров связанного javadoc для обоих случаев.

1 голос
/ 15 марта 2009

Это просто не работает так, как вы думаете. Из Javadoc wait() метод (выделение мое):

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

Очевидно, что в вашей программе нет другого потока, чтобы разбудить спящий compute() метод.

Чтобы решить вашу конкретную проблему, вам нужно либо иметь два потока, либо, альтернативно, реализовать метод compute() как возобновляемый, что-то подобное в псевдо-Java:

ComputeStatus status = new ComputeStatus();
do {
    compute(status);  // compute iteration
    mark(status);     // draw iteration
    status.next();    // next iteration
} while (!status.isFinished());

Здесь ComputeStatus содержит текущее состояние вычислений, а comupte() знает, как продолжить вычисление из этого состояния. Изменяете ли вы статус в compute() или в основном цикле, решать вам и решаемой вами проблеме.

0 голосов
/ 16 марта 2009

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

Это довольно просто. mark (int var) уже будет завершен к тому времени, когда вы дойдете до команды wait (), поэтому notify () в mark (int var) не сможет его разбудить.

0 голосов
/ 16 марта 2009

Функция wait () никогда не освобождается, поскольку вы не синхронизируете один и тот же объект. Ваш метод вычисления находится в другом объекте, и поэтому вызов notify не использует тот же монитор, что и ваш метод mark ().

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

Единственный способ, которым wait () будет «просыпаться», это если другой поток вызывает notify () из блока синхронизации для того же объекта.

0 голосов
/ 15 марта 2009

Метод repaint регистрирует необходимость рисовать компонент, но на самом деле он не рисует его, однако Java перекрасит объект при следующей возможности. Если вы пытаетесь создать что-то похожее на анимацию, то нет смысла ждать завершения перекраски. Вместо этого я рекомендую вам использовать таймер. Теперь у вас есть 2 варианта для таймеров. Если вы обновляете что-то, где время не должно быть точным, то часто вы ищете javax.swing.Timer. Вы используете это так:

//imports (before class definition)
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

//put this code where you want to start doing calculations
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
    public void actionPerformed(ActionEvent evt) {
        //update your calculations
        model.calculate();
        //tell Java to call paint at the next chance it gets
        viewer.repaint();
    }
};
new Timer(delay, taskPerformer).start();

В приведенном выше коде модель - это объект, для которого вы хотите выполнить вычисления, а средство просмотра - это объект, который рисует на основе модели.

Таймер свинга не очень точен по времени, что хорошо для многих вещей, но иногда вам нужно, чтобы код был более точным. В этом случае вы можете использовать класс java.util.Timer. Вы используете это так:

//imports (before class definition)
import java.util.Timer;
import java.util.TimerTask;

//inner class that does the calculations
public class CalculateTask extends TimerTask {
    public void run() {
        model.calculate();
        view.repaint();
    }
}

//put this code where you want to start doing calculations
int delay = 0;//time before running CalculateTask.run()
int repeat = 1000; //time between each subsequent rums of CalculateTask.run()
boolean isDaemon = true;//allow java to shutdown without stopping timer
Timer timer = new Timer(isDaemon);
timer.scheduleAtFixedRate(new CalculateTask(), delay, repeat); 
0 голосов
/ 15 марта 2009

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

Если вы не используете потоки, то вы не можете использовать ожидание / уведомление или любой другой вид синхронизации, поскольку нет двух потоков для синхронизации. Но вам не обязательно явно использовать потоки в программе с графическим интерфейсом, чтобы в конечном итоге использовать потоки.

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

У вас есть одна из следующих проблем:

1) Вы используете потоки, не зная об этом, и вы используете два разных объекта монитора. Таким образом, когда вы вызываете notify(), он уведомляет монитор об этом объекте, но не о первом объекте, для которого вы вызываете wait(). Есть много возможных решений. Одним из самых простых является использование для этого утилит параллелизма JDK 5, которые НАМНОГО лучше, чем встроенные базовые методы ожидания / уведомления монитора. Или,

2) Вы работаете в одном потоке, и ожидание / уведомление не годится. Просто в однопоточной программе не имеет смысла ждать, пока другой поток уведомит об этом, - нет другого потока, который может это сделать.

Предполагая, что вы на самом деле используете более одного потока, хороший способ решить эту проблему с помощью Java 5 и более поздних версий - использовать семафор , возможно, с помощью класса, содержащего mark(), и немного упростить :

private final Semaphore sem = new Semaphore(1);

public void mark(int var) {
  sem.acquire();
  //change some stuff
  repaint();
  sem.release();
}

waitForSemaphore() {
  sem.acquire();
}

, а затем в compute, позвоните waitForSemaphore(), когда хотите дождаться уведомления mark(). Поскольку mark() уже приобрел семафор, вам придется подождать, пока mark() освободит семафор, прежде чем вычисления смогут получить его, вызвав waitForSemaphore().

0 голосов
/ 15 марта 2009

Object.wait () и Object.notify () предназначены только для потоков. В показанном вами коде метка метода (int var) не вернется, пока не будет завершена, не нужно ждать. Кроме того, синхронизированные методы необходимы только в многопоточной программе.

Ваш код должен быть:

public void compute()
{
    other.mark(variable);
}

public void mark(int var)
{
    //change some stuff
    repaint();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...