【问题标题】:Java: Is it common practice to create a new Thread when a lock is held?Java:在持有锁时创建一个新线程是常见的做法吗?
【发布时间】:2010-02-26 08:15:32
【问题描述】:

我有一个与我在这里的第一个问题相关但不完全相同的问题:

Java: what happens when a new Thread is started from a synchronized block?

当您持有锁时,创建和start() 一个新的Thread 是一种常见的做法吗?

那会是代码味道吗?

基本上我可以选择这样做:

public synchronized void somemethod() {

    (every time I find a callback to be notified I start a thread)   
    Thread t = new Thread( new Runnable() {
       void run() {
           notifySomeCallback();      
       }
    }
    t.start();
    ... 
    (lengthy stuff performed here, keeping the lock held)
    ...
}

或者这个:

public void somemethod() {
    (create a list of callbacks to be notified)
    synchronized(this){
      (potentially add callbacks)       
      ... 
      (lengthy stuff performed here, keeping the lock held)
      ...
    }
    (notify the callbacks without holding a lock and once
     we know the lock has been released)
}

我认为后者更好,但我想知道是否有 是第一个选项可以的情况吗?你有时会 去做?你见过吗?

【问题讨论】:

    标签: java multithreading locking callback


    【解决方案1】:

    答案3:

    • 您应该始终保持尽可能短的锁。因此,当存在“损坏”资源的可能性时(例如,编写器线程正在更新资源),只有可能从多个线程引用的资源才应该被锁定最短的时间

    • 不要为每件需要做的小事都抽出一个线程。对于您的回调线程,让 1 个回调线程处理一系列待办事项。

    【讨论】:

      【解决方案2】:

      你知道sn-ps这两个代码会导致不同的执行顺序。 第一个将异步运行回调,同时执行冗长的工作。第二个将先完成冗长的工作,然后调用回调。

      哪个更好取决于回调需要做什么。如果他们需要先完成冗长的工作,这很可能是个问题。

      谁在等锁? 如果回调需要锁才能运行,那么在您仍然持有锁的情况下触发它们几乎没有意义。他们所要做的就是等待冗长的事情完成。

      另外,在第一个 sn-p 中,每个回调都有一个线程。第二个 sn-p 不是显式的,但是如果您对所有这些都只有一个线程,这是另一个区别 (回调是同时运行还是按顺序运行)。如果它们都需要相同的锁,您不妨按顺序运行它们。

      如果您想使用一个或多个线程运行多个回调,请考虑使用 Executor 而不是自己管理线程。使配置适当数量的线程变得非常容易。

      【讨论】:

      • @Thilo:是的,我知道不同的执行顺序。基本上一切都是/可以是异步的,因此不同的顺序不是问题,谢天谢地。许多回调正在触发,因此我认为它们可能会触发可能导致我的第一个示例中的死锁的事情。由于您的 (+1) 回答,我现在也意识到我正在创建不必要的线程。我会考虑让他们排队。
      【解决方案3】:

      这取决于您是否希望回调与冗长的内容同时执行。如果我们谈论的是 Swing GUI,选项 1 不好,因为您不应该在多个并发线程中执行 Swing 操作,所以我建议如下:

      public void somemethod() {   
         Thread t = new Thread( new Runnable() {
             void run() {               
                 doLengthyStuff();
             }
          }
          t.start();
      
          (notify the callbacks)
      }
      

      【讨论】:

      • 您可以简单地删除同步块。您在新创建的 Runnable 上进行同步,任何其他线程都不会使用该 Runnable 进行同步。
      • sfussenegger:这样做会完全违背锁定的目的。
      • 什么会破坏锁定的目的?您是否意识到您根本没有锁定任何内容?您在匿名内部类 (Runnable) 的实例上持有一个监视器,该实例是在每次调用 somemethod() 时新创建的,因此,您可能有多个线程同时执行 doLengthyStuff()。如果您想确保只有一个线程正在执行doLengthyStuff(),则应将同步移至该方法本身——并且它应该始终使用同一个监视器。 (顺便说一句,如果您希望作者以外的其他人得到通知,请确保使用@user)
      • @ammoQ: sfussenegger 指出,在您的示例中,您的同步没有任何作用,因为“this”指的是您创建的新 Runnable 对象,并且任何其他对象都不可能锁定该对象,因为无法获取对匿名 new Runnable() 实例的引用。
      • 我已将两个示例程序粘贴到 pastebin.org 以说明这一点:您的建议:pastebin.org/98208 正确的解决方案:pastebin.org/98210
      猜你喜欢
      • 1970-01-01
      • 2013-07-19
      • 1970-01-01
      • 2015-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多