仅当线程尚未运行时才在线程中运行更新操作

时间:2013-01-23 19:13:30

标签: java multithreading thread-safety

收到通知时,我需要异步执行更新操作。下面的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块的第一个线程将获取锁定,启动操作并释放锁定。然后第二个(或更多)将获取锁定,查看操作正在进行,重新计划并释放锁定。

此设置是否会失败?

3 个答案:

答案 0 :(得分:3)

  

此设置是否足以同时运行两(2)次更新操作?

否,因为您锁定了对象。您应该始终在非最终对象上进行同步,永远Boolean上进行同步。随着isInProgress的值发生变化(因为它设置为truefalse),多个线程将锁定不同的对象,并且可以同时进入互斥块。

如果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个对象,并带来奇怪的结果。

有关详细信息,请参阅我的回答:

  

Why is it not a good practice to synchronize on Boolean?

答案 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();
}
相关问题