Java - 线程+动作

时间:2011-06-05 22:22:57

标签: java

我是Java的新手,所以我有一个简单的问题,我不知道从哪里开始 - 我需要编写一个接受Action的函数,在多线程程序中,只有进入函数的第一个线程执行操作,所有其他线程等待他完成,然后从函数返回而不做任何东西。

正如我所说 - 我不知道从哪里开始因为, 首先 - 在函数中没有静态var(静态就像在c / c ++中那样)所以如何让它只有第一个线程才能启动动作,其他什么都不做? 第二 - 对于要等待的线程,我应该使用

public synchronized void lala(Action doThis)
{....}

或者我应该在函数

中写出类似的东西
synchronized (this)
{
...
notify();
}

谢谢!

4 个答案:

答案 0 :(得分:3)

如果希望到达方法的所有线程都等待第一个,那么它们必须在公共对象上同步。它可以是调用方法的相同实例(this),也可以是任何其他对象(显式锁对象)。

如果你想确保第一个线程是唯一一个将执行该操作的线程,那么你必须将这个事实存储在某个地方,以便所有其他线程读取,因为它们将执行相同的指令。

按照前两点,可以锁定这个“事实”变量以达到预期的结果

static final AtomicBoolean flag = new AtomicBoolean(false); // synchronize on this, and also store the fact. It is static so that if this is in a Runnable instance will not appear to reset the fact. Don't use the Boolean wrapper, for the value of the flag might be different in certain cases.

public void lala(Action doThis)
{
    synchronized (flag) // synchronize on the flag so that other threads arriving here, will be forced to wait
    {
        if(!flag.get()) // This condition is true only for the first thread.
        {
           doX();
           flag.set(true); //set the flag so that other threads will not invoke doX.
        }
    }
    ...
    doCommonWork();
    ...
}

答案 1 :(得分:3)

如果你在任何最新版本的Java中进行线程处理,你真的应该使用java.util.concurrent包而不是直接使用Thread

这是你可以做到的一种方式:

private final ExecutorService executor = Executors.newCachedThreadPool();
private final Map<Runnable, Future<?>> submitted 
    = new HashMap<Runnable, Future<?>>();

public void executeOnlyOnce(Runnable action) {
    Future<?> future = null;
    // NOTE: I was tempted to use a ConcurrentHashMap here, but we don't want to
    // get into a possible race with two threads both seeing that a value hasn't
    // been computed yet and both starting a computation, so the synchronized
    // block ensures that no other thread can be submitting the runnable to the
    // executor while we are checking the map.  If, on the other hand, it's not
    // a problem for two threads to both create the same value (that is, this
    // behavior is only intended for caching performance, not for correctness),
    // then it should be safe to use a ConcurrentHashMap and use its
    // putIfAbsent() method instead.
    synchronized(submitted) {
        future = submitted.get(action);
        if(future == null) {
            future = executor.submit(action);
            submitted.put(action, future);
        }
    }
    future.get(); // ignore return value because the runnable returns void
}

请注意,这假定您的Action类(我假设您不是javax.swing.Action,对吗?)实现Runnable并且合理实现{{1} }和equals()。否则,您可能需要使用其他hashCode()实现(例如,Map)。

此外,这假设您可能只有一次要执行的多个不同操作。如果情况并非如此,那么您可以完全删除地图并执行以下操作:

IdentityHashMap

答案 2 :(得分:1)

public synchronized void foo()
{
 ...
}

相当于

public void foo()
{
     synchronized(this)
     {
          ... 
     }
}

所以这两个选项中的任何一个都应该有用。我个人喜欢同步方法选项。

答案 3 :(得分:1)

如果只有代码的某一部分处理共享数据(例如,每个线程正在更新的公共变量),同步整个方法有时可能会过度。

性能的最佳方法是仅在共享数据周围使用synchronized关键字。如果你在完全不一定的情况下同步了整个方法,那么很多线程在他们仍可以在自己的本地范围内工作时会等待。

当一个线程进入同步时,它获取一个锁(如果你使用这个对象它锁定在对象本身上),另一个将等到锁获取线程退出。在这种情况下,您实际上不需要 notify 语句,因为线程将在退出synchronize语句时释放锁定。