我从之前的开发人员(lol)继承了这段代码。我正在考虑更改它以支持连接而不是使用侦听器类型的回调。
我的要求: 1.我需要让调用线程等到DoMath类线程完成。 2.我需要阻止其他线程调用它。
这,在另一个线程(和类)中: -
DoMath.getInstance().performMathCalc();
当它调用它时,它不会等待或睡觉:
public class DoMath {
protected math calc() {
}
public static DoMath getInstance() {
if(_instance == null) {
_instance = new DoMath();
}
return _instance;
}
// perform a synchronous math calc, and return a boolean indicating success or failure.
public boolean performMathCalc() {
MathEngine.setApplicationMode(MathEngine.AUTO);
MathEngine.getInstance().StartMathCalc(MathEngine.DIVISION);
return true;
}
// perform an async math calc, and call back the listener when done
public void performMathCalc(final listener client) {
Thread mathThread = new Thread(new Runnable() {
public void run() {
boolean result = performMathCalc();
client.mathFinished(result);
}
});
mathThread.setDaemon(true);
mathThread.start();
}
public static interface listener {
public void mathFinished(boolean success);
}
protected static DoMath _instance;
}
那么,在调用类中使用监听器或实现连接是否更好?
答案 0 :(得分:1)
我需要让调用线程等到DoMath类线程完成。
你已经有了这个。请注意两个 performMathCalc
方法如何:
第一个方法不带参数,并在调用程序线程上执行计算,然后返回结果。这符合您的第一个要求。
第二种方法是第一种方法的异步包装;它允许呼叫者启动计算,然后在做出其他事情的情况下,在将来的某个时刻,有人会被告知操作已经完成。这是有用的功能,所以我会保留它。
我确实看到异步包装器存在一个问题:如果核心performMathCalc()
方法抛出异常,则不会通知侦听器。考虑使用try/catch/finally
块来确保始终收到通知,即使发生错误也是如此。您需要决定是否向听众添加第二个回调(例如mathFailed
),或者只是在出错时调用mathFinished(false)
。
我需要阻止其他线程调用它。
我们可以很容易地实现这一点,并且由于异步版本只是委托给同步版本,我们只需要锁定同步版本。最简单的方法是将方法标记为synchronized
,因为您的类只提供一个逻辑函数:
public synchronized boolean performMathCalc() {
MathEngine.setApplicationMode(MathEngine.AUTO);
MathEngine.getInstance().StartMathCalc(MathEngine.DIVISION);
return true;
}
或者,如果您最终扩展DoMath
类以执行非互斥的其他类型的操作,则可以在特定于操作的锁上进行同步。
这给我们留下了你的单身访问者:
public static DoMath getInstance() {
if (_instance == null) {
_instance = new DoMath();
}
return _instance;
}
此条件初始化不是线程安全的。您的单例非常简单,并且没有任何前期初始化成本,因此只需将_instance
标记为final static
并在声明中对其进行初始化。
答案 1 :(得分:1)
你真的想暂停你的线程直到另一个完成吗?你永远不应该阻止主线程。
The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,
t.join();
causes the current thread to pause execution until t's thread terminates. Overloads of join allow the programmer to specify a waiting period. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.
(来自java docs)
另外,performMatchCalc()是否需要公开?
现在,乍一看代码实际上看起来是正确的,但是,您仍然可以阻止某人开始另一次计算。也许有类似的东西:
public class DoMath {
private Thread mathThread;
protected math calc() {
}
public static DoMath getInstance() {
if(_instance == null) {
_instance = new DoMath();
}
return _instance;
}
// perform a synchronous math calc, and return a boolean indicating success or failure.
public boolean performMathCalc() {
if(null != mathThread && mathThread.isAlive())
return false;
MathEngine.setApplicationMode(MathEngine.AUTO);
MathEngine.getInstance().StartMathCalc(MathEngine.DIVISION);
return true;
}
// perform an async math calc, and call back the listener when done
public void performMathCalc(final listener client) {
//re-start calculation? if so
if(null != mathThread && mathThread.isAlive()) {
matchThread.interrupt();
matchThread = null;
}
mathThread = new Thread(new Runnable() {
public void run() {
boolean result = performMathCalc();
client.mathFinished(result);
}
});
mathThread.setDaemon(true);
mathThread.start();
}
public static interface listener {
public void mathFinished(boolean success);
}
protected static DoMath _instance;
}
答案 2 :(得分:1)
请注意:
public static DoMath getInstance() {
if(_instance == null) {
_instance = new DoMath();
}
return _instance;
}
不是线程安全的。要确保您的类确实是Singleton(相对于其ClassLoader),您必须同步该方法或在其声明中初始化_instance
成员。不管怎样,_instance
必须是private
或final
或两者兼而有之。
至于你的实际要求,
(1)似乎你想要将异步调用更改为同步调用,或者在它周围放置一个同步包装器。您可以通过现有的侦听器接口执行后者,这将保留执行异步作业的能力。如果您不想要它而不是加入,请跳过启动新线程:只需在当前线程中运行计算。
(2)如何防止多个线程同时运行计算,部分取决于您如何解决问题(1)。如果您使所有内容同步,那么您可以使DoMath.performMathCalc()
成为同步方法。如果您保留异步计算选项,那么您可以查看将java.util.concurrent.locks打包为可以帮助您的类。