【问题标题】:Java synchronization: how to have waiting threads not execute the synchronized taskJava同步:如何让等待线程不执行同步任务
【发布时间】:2021-01-02 04:06:25
【问题描述】:

假设我有一段 Java 代码想要同步执行,但是当拥有锁的线程释放该锁时,我不想再等待线程执行同步代码。我只是希望他们等到同步代码完成运行。

因此,当线程 1 进入同步代码(方法、代码块)时,它必须锁定该代码。任何后续线程都必须等待第一个线程完成运行代码。至此,定期代码同步。

但是在我的例子中,当第一个线程完成并释放锁时,我希望等待的线程跳过同步的代码段(代码更新所有线程使用的资源)。

我将如何使用同步功能在 Java 中创建它?目前我没有使用同步功能,而是使用持有锁(上下文)的并发集合,如下所示。但是,锁定的过程仍然需要以某种方式同步,因为两个线程能够获得上下文的锁定。

干杯!

凯尔德

    /*
     * A concurrently accessible set of contexts.
     */
    private static final Set<String> LOCKED_CONTEXTS;

    static {
        LOCKED_CONTEXTS = ConcurrentHashMap.newKeySet(); // meanwhile, I replaced this with Collections.synchronizedSet(new HashSet<>()) 
    }

...

        if (!LOCKED_CONTEXTS.contains(context)) {

            LOCKED_CONTEXTS.add(context);
            log.debug("Locked context: {}", context);

            try {
                doTask();

            } finally {

                LOCKED_CONTEXTS.remove(context);
                log.debug("Released context: {}", context);
            }
        } else {

            log.debug("Waiting for context to be released: {}", context);

            while (LOCKED_CONTEXTS.contains(context)) {
            }

            log.debug("The waiting is over, context is released: {}", context);
        }

【问题讨论】:

  • 同时,通过用同步集替换并发集,我得到了上述解决方法来做我想做的事。但是解决方法对我来说并不理想。现在正在研究 Semaphore 建议...
  • 同步在一个对象上,用于杂项代码。这里似乎关注一段代码,一个关键区域?通常人们会在同步一条 if 语句之后看到,反映第一次演练的条件已经产生了一个东西。

标签: java synchronization


【解决方案1】:

我想你想把锁和信号量结合起来。

  • tryLock 上锁
  • 如果你明白了,那就去做吧。在工作结束时,将信号量标记为完成
  • 如果没有得到,请等待信号量完成

【讨论】:

  • ReentrantLock,因为它被明确记录为“与使用同步方法和语句访问的隐式监视器锁具有相同的基本行为和语义,但具有扩展功能”,其中tryLock是这些能力之一。
  • 好吧,但是我如何获得特定于上下文的锁?假设一个线程在 context='abc' 上有一个锁,所以其他线程必须等待它被释放,但是另一个上下文 'def' 没有被锁定,因此允许当前线程声明一个上下文,如果上下文是'def'。也许编写我自己的特定于上下文的 ReentrantLock 实现?
【解决方案2】:

也许我不完全理解您的用例,但据我所知,您希望一段代码在其执行期间仅针对所有触发器运行一次,但如果任何触发器发生在前一个执行窗口之外,则再次运行。

这包括它吗?

class Work {
  private volatile boolean done;

  void queueWorkOnce() {
    done = false;
    actualWork();
  }

  private synchronized void actualWork() {
    if (!done) {
      //TODO: do work
      done = true;
    }
  }
}

【讨论】:

  • 这似乎很有用。但是,行为必须依赖于给定的上下文字符串。所以我在想,我是否可以用 Map 替换布尔“完成”,它在字符串和布尔状态之间保存引用?
  • 只需将锁定放置在您需要唯一执行的任何内容上。因此,如果是每个上下文,您可以使用 sychronized(contextString) {} 而不是方法级别。就个人而言,我可能更愿意为每个上下文制作一个 Work 对象,但如果这是可能的,因为这是更好的形式。
  • 我接受这个答案(在其他非常好的建议旁边),因为我设法通过将您的解决方案应用于上下文(根据要求)在更广泛的范围内成功使用它。还有一个问题:您能否详细说明为什么需要使用 volatile 关键字?
  • 它主动禁止虚拟机对对象级变量值进行任何方法内级缓存,从而确保您不会丢失来自不同线程的对象级变量的变量更新。因此,我通常将它包含在由不同线程变异的变量中,也将它们标记为其他开发人员。在这种情况下,防止(理论上的)“假”值被脏读,以防值复制发生在锁定之前。 JVM 规范甚至可能不允许的东西,只是不是 100% 肯定我会说添加它更好。
猜你喜欢
  • 1970-01-01
  • 2019-04-09
  • 1970-01-01
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 2015-07-13
  • 2012-04-15
相关资源
最近更新 更多