[英]Is it possible to wait for methods that aren't in Threads to finish in Java?
我有一個進行一些計算的對象,然后對於每個迭代,我想畫出發生的情況。 在繪制過程中,我希望它等待。
這是我所做的,基本上是:
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,所以也許是問題所在嗎? 我不確定,因為我認為如果這些對象不能使用此類方法,我會被警告。
我想這可能是關於程序本身的邏輯的錯誤,但總的來說,這就是我所擁有的。
您的問題建議您要執行一些操作,以在完成時更新GUI狀態或將其進度通知給GUI。 這就是SwingWorker的目的。 對於這兩種情況,鏈接的javadoc上都有一些示例。
這根本無法像您想象的那樣起作用。 從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()
還是在主循環中更改狀態,都取決於您和您要解決的問題。
Object.wait()和Object.notify()僅供線程使用。 在顯示的代碼中,mark(int var)方法直到完成才返回,無需等待。 此外,僅在多線程程序中才需要同步方法。
您的代碼應為:
public void compute()
{
other.mark(variable);
}
public void mark(int var)
{
//change some stuff
repaint();
}
由於您的程序是一個GUI程序(我通過repaint()
調用收集),因此它固有地是多線程的,即使您不知道它也是。 (如果沒有,它將表現得很差。)
如果您不使用線程,則不能使用等待/通知或任何其他類型的同步,因為沒有兩個線程可以同步。 但是,不必一定要在GUI程序中顯式地使用線程來最終使用線程。
請注意,如果您使用依賴線程的方法,但實際上不以任何方式使用線程,則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
,當您想等待被mark()
通知時,調用waitForSemaphore()
mark()
。 因為mark()
已經獲取了信號量,所以您必須等待mark()
釋放信號量,然后計算才能通過調用waitForSemaphore()
來獲取它。
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();
在上面的代碼中,模型是您要對其執行計算的對象,查看器是根據模型進行繪制的對象。
Swing計時器的計時不是很精確,這在很多情況下都很好,但是有時您需要更精確地計划代碼。 在這種情況下,您可能要使用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);
由於您沒有在同一個對象上進行同步,因此永遠不會釋放wait()。 您的計算方法位於另一個對象中,因此,通知調用與mark()方法共享的監視器不同。
等待/通知機制用於共享監視器,也就是說,它們必須共享相同的線程鎖定機制。
wait()將“喚醒”的唯一方法是,如果另一個線程從同一對象上的同步塊內調用notify()。
不幸的是,wait()永遠不會停止等待。 主要原因是,看您是否放置了notify()。 它是由同一線程調用的,無法將其自身喚醒。
這很簡單。 mark(int var)在您到達wait()命令時已經完成運行,因此mark(int var)中的notify()無法喚醒它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.