如果某些线程在Java中的`methodA`中,如何确保`methodB`被“阻塞”?

时间:2016-09-28 08:52:07

标签: java multithreading synchronization locking

Class clazz有两种方法methodA()methodB()

  

如果某些线程在Java methodB中(我使用的是Java 8),如何确保methodA被“阻止”?

通过“阻塞方法B”,我的意思是“等到没有线程在methodA()中”。 (感谢@AndyTurner)

请注意,上面的要求允许以下情况:

  1. 多个帖子同时在methodA
  2. methodB中有多个线程,而methodA中没有线程。
  3. methodB中的主题不会阻止其他主题进入methodA
  4. 我的试用版:我使用StampedLock lock = new StampedLock

    • methodA中,请致电long stamp = lock.readLock()
    • 创建新方法unlockB并在其中调用lock.unlockRead(stamp)
    • methodB中,拨打long stamp = lock.writeLock()lock.unlockWrite(stamp)

    但是,这种锁定策略不允许上述第二种和第三种情况。

    编辑:我意识到我没有明确指出methodAmethodB之间同步的要求。 @JaroslawPawlak给出的方法适用于当前的要求(我接受它),但不符合我的初衷(也许我应该先澄清它,然后将其发布在另一个帖子中)。

5 个答案:

答案 0 :(得分:4)

我认为这可以解决问题:

private final Lock lock = new ReentrantLock();
private final Semaphore semaphore = new Semaphore(1);
private int threadsInA = 0;

public void methodA() {
    lock.lock();
    threadsInA++;
    semaphore.tryAcquire();
    lock.unlock();

    // your code

    lock.lock();
    threadsInA--;
    if (threadsInA == 0) {
        semaphore.release();
    }
    lock.unlock();
}

public void methodB() throws InterruptedException {
    semaphore.acquire();
    semaphore.release();

    // your code
}

进入methodA的线程会增加计数并尝试从信号量获取许可证(即如果可用则获得1个许可证,但如果不可用,则仅在没有许可证的情况下继续)。当最后一个线程离开methodA时,返回许可证。我们不能使用AtomicInteger,因为更改计数和从信号量获取/释放许可必须是原子的。

进入methodB的主题需要有一个许可(如果不可用则会等待一个),但是在获得之后,他们立即返回,允许其他人进入methodB

修改

另一个更简单的版本:

private final int MAX_THREADS = 1_000;
private final Semaphore semaphore = new Semaphore(MAX_THREADS);

public void methodA() throws InterruptedException {
    semaphore.acquire();

    // your code

    semaphore.release();
}

public void methodB() throws InterruptedException {
    semaphore.acquire(MAX_THREADS);
    semaphore.release(MAX_THREADS);

    // your code
}

methodA中的每个帖子都拥有一个许可证,当该帖子离开methodA时会被释放。

进入methodB的线程等到所有1000个许可都可用(即methodA中没有线程),但不保留它们,这允许其他线程在methodB时输入这两个方法仍在执行中。

答案 1 :(得分:0)

为什么不使用一种外部协调器? 我的意思是另一个类负责在允许时调用methodA或methodB。 多线程仍然可以通过锁定处理,也可以只使用一些AtomicBoolean(。)。

请在下面找到如何做的天真草稿。

public class MyOrchestrator {

@Autowired
private ClassWithMethods classWithMethods;

private AtomicBoolean aBoolean = = new AtomicBoolean(true);

 public Object callTheDesiredMethodIfPossible(Method method, Object... params) {
   if(aBoolean.compareAndSet(true, false)) {
      return method.invoke(classWithMethods, params);
      aBoolean.set(true);
   }
   if ("methodA".equals(method.getName())) {
      return method.invoke(classWithMethods, params);
   }
 } 

}

答案 2 :(得分:0)

你无法真正阻止调用methodA或methodB(而其他线程在其他方法中),但你可以通过这种方式实现线程互通,这样你仍然可以实现你想要的。

class MutualEx {
   boolean lock = false;

   public synchronized void methodA() {
      if (lock) {
         try {
            wait();
         }catch (InterruptedException e) {

         }
      }
      //do some processing
      lock = true;
      notifyAll();
   }

   public synchronized void methodB() {
      if (!lock) {
         try {
            wait();
         }catch (InterruptedException e) {

         }
      }

       //do some processing
      lock = false;
      notifyAll();
   }
}

现在,为了使这个工作,您创建的任何Thread对象都应该引用MutualEx对象的相同实例。

答案 3 :(得分:0)

简单来说,您需要的是proc c {} { namespace upvar :: x x y y expr {$x + $y} }

  • 只需要一个全局计数器,首先将其初始化为0,以记录当前位于a内的线程数。您应该分配ENTER methodB only if no thread inside methodA来保护变量methodA()
  • 进入方法的线程A执行lock/mutex
  • 退出方法A的主题执行count
  • 首先进入methodB的线程应检查count++

    count--

答案 4 :(得分:0)

你需要一个int来计算methodA中的线程,并且一旦在methodA中没有线程,你需要ReentrantLock.Condition来表示在methodB中等待的所有线程:

AtomicInteger threadsInMethodA = new AtomicInteger(0);
Lock threadsForMethodBLock = new ReentrantLock();
Condition signalWaitingThreadsForMethodB = threadsForMethodBLock.newCondition();

public void methodA() {
    threadsInMethodA.incrementAndGet();
    //do stuff
    if (threadsInMethodA.decrementAndGet() == 0) {
        try {
            threadsForMethodBLock.lock();
            signalWaitingThreadsForMethodB.signalAll();
        } finally {
            threadsForMethodBLock.unlock();
        }
    }
}

public void methodB() {
    try {
        threadsForMethodBLock.lock();
        while (!Thread.isInterrupted() && threadsInMethodA.get() != 0) {
            try {
                signalWaitingThreadsForMethodB.await();
            } catch (InterruptedException e) {
                Thread.interrupt();
                throw new RuntimeException("Not sure if you should continue doing stuff in case of interruption");
            }
        }
        signalWaitingThreadsForMethodB.signalAll();
    } finally {
        threadsForMethodBLock.unlock();
    }
    //do stuff

}

因此,进入methodB的每个线程将首先检查methodA中是否有人,并发出先前等待线程的信号。另一方面,进入methodA的每个线程将递增计数器以防止新线程在methodB中工作,并且在递减时,如果没有线程留在methodA中,它将释放等待在methodB中执行东西的所有线程。