收到通知时,我需要异步执行更新操作。下面的update()
方法操纵实例变量。
public class UpdateOperation implements Runnable
{
private Boolean isInProgress = false;
@Override
public void run() {
try
{
synchronized (isInProgress)
{
isInProgress = true;
}
update(); //perform update
synchronized (isInProgress)
{
isInProgress = false;
}
}
catch (UpdaterException e)
{
// deal with it
}
}
}
// In another class
private UpdateOperation mCurrentUpdateOperation = new UpdateOperation();
public void updateRequired()
{
synchronized (mCurrentUpdateOperation.isInProgress)
{
if (!mCurrentUpdateOperation.isInProgress)
{
new Thread(mCurrentUpdateOperation).start();
}
else
{
// reschedule or silently ignore
}
}
}
此设置是否足以同时运行两(2)次更新操作?我相信这是因为到达synchronized
块的第一个线程将获取锁定,启动操作并释放锁定。然后第二个(或更多)将获取锁定,查看操作正在进行,重新计划并释放锁定。
此设置是否会失败?
答案 0 :(得分:3)
此设置是否足以同时运行两(2)次更新操作?
否,因为您锁定了对象。您应该始终在非最终对象上进行同步,永远在Boolean
上进行同步。随着isInProgress
的值发生变化(因为它设置为true
或false
),多个线程将锁定不同的对象,并且可以同时进入互斥块。
如果UpdateOperation
实例可以final
,则可以锁定它。您可以随时执行以下操作:
private final Object lockObject = new Object();
...
synchronized (lockObject) {
...
}
锁定对象后,您可以检查inProgress
的状态,该状态可能是布尔基元。 synchronize
构造同步所有内存。有关详细信息,请参阅Java thread tutorial on synchronization。
锁定Boolean
特别糟糕,因为在整个JVM中只有2个常量对象引用(除非你new Boolean(...)
)。当你说:
isInProgress = true;
你实际上在说:
isInProgress = Boolean.TRUE;
因此,所有类中的所有线程都会锁定相同的2个对象,并带来奇怪的结果。
有关详细信息,请参阅我的回答:
答案 1 :(得分:1)
看看Executor。他们将提供一个Threadpool,您只需添加runnable即可。您也可以使用AtomicInteger。
我认为这将为您提供您想要的一切,更易于使用。
答案 2 :(得分:1)
原始解决方案的另一个问题是检查和设置isInProgress
变量是在不同的同步语句中,这会产生时间间隔。因此,可以启动多个线程。
正确的解决方案是:
public class UpdateOperation implements Runnable {
private boolean inProgress = false;
public void start() {
synchronized (this) {
if (inProgress) {
return;
}
inProgress=true;
}
new Thread(this).start();
}
@Override
public void run() {
try {
update(); //perform update
} catch (UpdaterException e) {
// deal with it
} finally {
synchronized (this) {
inProgress = false;
}
}
}
}
// In another class
private UpdateOperation mCurrentUpdateOperation = new UpdateOperation();
public void updateRequired() {
mCurrentUpdateOperation.start();
}