做GUI程序的时候,通常有个后台工作线程在努力工作,但是中间又需要一些暂停,而关闭程序的时候,必须立即结束那个线程,退出程序,也有的时候需要停止后台线程,但不关闭程序。例如,做一个目录监控程序,发现目录中有文件的时候,执行一定的操作,执行完之后没有文件了,就要暂停一下,过几秒或几分钟再次检测,这时候就要对线程进行暂停操作,如果在暂停的时候,用户要关闭程序,就必须马上停止线程,如果用户需要暂停检测,按下某个按钮后,需要让线程马上停止,但再次按下某个按钮,线程又必须马上开始。
以前我都是通过检测停止标记和用Thread.sleep(time)来完成的,后台线程的每次循环都要检查停止标记,如果发现停止标记已设定,就不再循环,退出线程,在线程内部,如果需要暂停,就执行Thread.sleep(time)。通过把线程的setDaemon(true)方法,还可以让线程作为后台线程,当图形界面关闭后,线程也自动退出。
但是,这种方式有个问题,如果我需要在图形界面上点击按钮来停止线程,但并不退出程序,而点击按钮的时候线程正处于sleep状态,就对它没有任何办法,只能让它醒过来再操作,如果sleep的时间比较长,例如1分钟,那么点击按钮之后,用户最多要等1分钟才能把线程停下来。
当然,Thread对象有个interrupt方法,但是已经被标记为过期,一般不建议使用了。感谢评论中的提醒,Thread的interrupt()并没有标记为过期,可以按照他的说法来操作,更为简单。
怎么让线程能暂停,又能随时叫醒呢?原来Java里最原始的对象Object就自带此功能。
每个Object都有wait(time)和notify()方法,前者就是让拥有该Obejct的线程处于暂停状态,后者则让线程马上唤醒,通过这两个方法,就能够满足上述的所有要求。
首先,建立一个同步对象:
Object syncObj = new Object();
然后在线程中需要暂停的地方,调用该对象的wait(time)方法:
synchronized (syncObj) {
syncObj.wait(60*1000);
}
在图形界面的按钮监听事件中,对该对象执行notify()方法:
button_1.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
thread.setStop(true);
synchronized (syncObj) {
syncObj.notify();
}
//为了等待线程退出,还可以加上以下语句:
thread.join();
}
});