为啥 Java 8 没有在 `java.util.concurrent.locks.Lock` 接口中添加 `withLock` 默认方法?

Posted

技术标签:

【中文标题】为啥 Java 8 没有在 `java.util.concurrent.locks.Lock` 接口中添加 `withLock` 默认方法?【英文标题】:Why didn't Java 8 add `withLock` default methods to the `java.util.concurrent.locks.Lock` interface?为什么 Java 8 没有在 `java.util.concurrent.locks.Lock` 接口中添加 `withLock` 默认方法? 【发布时间】:2014-06-04 09:51:56 【问题描述】:

和this question一样,我想知道为什么Java团队没有在Lock接口中添加一些默认方法,像这样:

public default void withLock(Runnable r) 
  lock();

  try 
     r.run();
   finally 
     unlock();
  

这将允许程序员这样做

public class ThreadSafe 
  // Object requiring protection
  private final List<String> l = new ArrayList<>();
  private final Lock lock = new ReentrantLock();

  public void mutate(String s) 
    lock.withLock(() -> l.add(s));
  

  public void threadSafeMethod 
    lock.withLock(() ->  System.out.println(l.get(l.size())); );
  

而不是

public void threadSafeMethod 
  lock.lock();
  try 
    System.out.println(l.get(l.size())); 
   finally  
    lock.unlock();
  

【问题讨论】:

【参考方案1】:

这个问题的答案似乎与the linked question 的答案相似——它被认为是“令人讨厌的麻烦”,因为从 lambda 创建的Runnable 的分配成本(参见Brian Goetz's response 到请求)。

好消息是这个请求有 an open bug,这意味着一旦 JVM 能够可靠地最小化分配成本,Oracle 似乎愿意重新考虑这个决定。

【讨论】:

作为旁注,Brian 的示例 withLock( () -&gt; counter++; ); 不可能是捕获 lambda,因为该示例中的 counter 不能是局部变量。但我们明白了这一点,实际上,你不能在这样的 withLock 中改变本地变量,或者发出 return 和其他提前退出控制语句的限制,可能是反对它的更有力的论据。跨度> @Marko Topolnik:示例() -&gt; counter++ 一个捕获 lambda 但它仅在 counter 是成员变量时才有效,因此 lambda 记住对 @987654332 的不可变引用@(或 static 变量的类)。请注意,如果 counter 是一个局部变量,那么不仅 lambda 不起作用,它还会使整个示例变得毫无意义,因为没有理由对 local 变量的更新应该由 @ 保护987654335@. @Holger 似乎存在术语不一致:我的意思是它不是一个 closure 因为它不会关闭局部变量(我认为这与“捕获 lambda”)。这意味着该示例没有展示任何性能问题,因此无法解释为什么 withLock 未包含在 API 中。如果counter 是本地变量,您的补充说明该示例将无效。 @Marko Topolnik:Brian Goetz 的解释针对的问题是,对于当前的 lambda 实现,当 lambda 具有状态 (see my explanation here) 时,每次调用都会创建一个新实例。不管是捕获this 还是counter,在这种情况下有用的lambda 总是有状态,因此每次都会产生一个新实例。所以他关于“捕获”的说法是正确的。但是,当所有其他 API 都忽略它时,我不明白为什么这个小性能问题会推动 API 设计决策…… @Holger 一个合理的预期是可以从this 引用的对象访问this 捕获的 lambda,而不涉及任何哈希表支持的 lambda 缓存。另外,我认为您使用的术语“调用站点”不是指调用站点,而是指 lambda 创建站点。

以上是关于为啥 Java 8 没有在 `java.util.concurrent.locks.Lock` 接口中添加 `withLock` 默认方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在此示例中我没有收到 java.util.ConcurrentModificationException?

java.util.Collection 为啥不实现新的 Stream 接口?

为啥我在使用 java.util.zip.ZipFile 打开空 ZIP 文件时出现异常?

为啥Java中没有SortedList?

为啥 Java 中有两个 Timer 类(一个在 javax.swing 下,一个在 java.util 下)?

为啥 java.util.concurrent.RunnableFuture 有一个 run() 方法?