简单地说,我想点击按钮来运行后台任务(分离的线程)。我面临两个问题:
- 如果用户多次点击按钮===>许多线程将创建
- 即使我使用Singleton机制,我也会面临另一个问题,即只有一次会创建该实例,即使在任务完成后,用户也无法再重新运行该过程(第二次单击按钮)
我的班级:
package mypack.check;
public class RunnableCheck implements Runnable {
private Thread t;
private static RunnableCheckFeeders instance;
public RunnableCheckFeeders getDefault() {
if (instance == null) {
instance = new RunnableCheckFeeders();
}
return instance;
}
@Override
public void run() {
//What the thread is supposed to do...
}
public void start() {
if (t == null) {
t = new Thread(this, "My task");
t.start();
}
}
}
在调用者类中:
RunnableCheckFeeders.getDefault().start();
我尝试了同步方法,但没有成功,任何提议都是受欢迎的。
我建议您使用ExecutorService。
enum RunOne {; // no instances
static final ExecutorService service = Executors.newSingleThreadedExecutor();
static Future last = null;
static synchronized void run(Runnable run) {
if (last != null && !last.isDone()) return;
last = service.submit(run);
}
}
只有在尚未运行新任务的情况下,才会提交新任务。它不会创建多个线程,但您可以在上一个线程完成后提交任务。您可以拨打service.shutdown()
停止服务。
即使只有一个RunnableCheck
实例,您的start
方法也不能保证只创建一个"我的任务"线程:因为线程引用和后续赋值的检查不是原子的,如果两个线程碰巧同时将t == null
求值为true(或者,至少,第二个线程可以在第一个线程能够将非null值分配给t
之前将其求值为true),则可以创建并启动两个线程。
您可以通过以下方式进行防护:
- 使
start
方法同步,因此多个线程不能同时运行该方法 -
添加一个
AtomicBoolean
来记录线程是否已创建/启动。通过原子更新该标志的值,两个线程不可能将其设置为true
,因此不可能创建和启动两个新的Thread
:private final AtomicBoolean started = new AtomicBoolean(); private Thread t; public void start() { if (!started.compareAndSet(false, true)) { return; } t = new Thread(this, "My task"); t.start(); }
当用户单击多次时,您希望事件发生多次还是只发生一次?如果多次,您不希望使用singleton,而是希望创建一个工作队列。
您可以通过以下两种方式之一来实现这一点,第一种是使用一个想要一个线程池,很可能是使用ExecutorService中的一个。
第二种选择是让一个队列从队列中读取,让一个线程从队列中进行读取。
如果您希望事件只发生一次,那么您需要禁用该按钮,直到它完成,使启动同步,以便一次只有一个线程可以调用它,然后在线程完成后将t
设置为null
(即run()
中的最后一项)。