0

为什么使锁对象私有封装锁以使客户端代码无法获取它?

对象的内在锁

   public class A{
        private final Set<Integer> set = new HashSet<Integer>();

        public synchronized void addInt(int i){
            set.add(i);
        }
   }

私人锁

   public class B{
        private final Set<Integer> set = new HashSet<Integer>();

        public void addInt(int i){
             synchronized(set){
                  set.add(i);
             }
        }
   }
4

3 回答 3

3

好吧,另一个类根本无法访问set,因为它是私有的。在许多其他人因为无法访问该引用而无法做的事情中,同步它就是其中之一。

如果 getter 直接返回该引用(无需包装或复制对象),则其他人可以对其进行同步,从而破坏了锁定私有对象的好处。

于 2012-08-19T17:08:43.337 回答
1

在您提供的两个示例中,都使用了内部监视器。然而,第二个例子使用了set字段的内在锁。使用这种做法时要小心:如果您发布对 的引用set,则类外的代码可能会在其监视器上同步,这可能导致死锁。

我认为你得到的区别是synchronized与使用java.util.concurrent.LockAPI 相比。您获得的大部分内容是增加了灵活性(来自Lock文档):

同步方法或语句的使用提供了对与每个对象关联的隐式监视器锁的访问,但强制所有锁的获取和释放以块结构的方式发生:当获取多个锁时,它们必须以相反的顺序释放,并且所有锁必须在获得它们的相同词法范围内释放。

虽然同步方法和语句的作用域机制使得使用监视器锁进行编程变得更加容易,并有助于避免许多涉及锁的常见编程错误,但在某些情况下,您需要以更灵活的方式使用锁。例如,一些遍历并发访问的数据结构的算法需要使用“hand-over-hand”或“链锁”:你获取节点 A 的锁,然后节点 B,然后释放 A 并获取 C,然后释放 B并获得 D 等等。Lock 接口的实现通过允许在不同范围内获取和释放锁以及允许以任意顺序获取和释放多个锁来启用此类技术。

还有两个方法调用为您提供了更多使用 Lock API 获取锁的方法:

  • lockInterruptibly: 除非线程被中断,否则获取锁。
  • tryLock: 仅在调用时空闲时获取锁(可选超时)。

Java 中并发的规范参考是“Java Concurrency in Practice”

于 2012-08-19T16:51:31.007 回答
0

为什么使锁对象私有封装锁以使客户端代码无法获取它?

老实说,我不知道这个问题的含义是什么。
您的代码片段之间的区别在于使用的锁。在第一种情况下,整个方法是同步的(使用A的内在锁),而在第二种情况下,您锁定了您尝试修改的集合。即 2 个线程可以同时进入该方法,但 1 个线程将阻止尝试获取add.
此方法有 2 个优点:1)它定义了锁定的粒度(对于只需要短时间锁定的长方法,在方法调用的整个持续时间内锁定会降低性能)和2)通过锁定收集你让它线程安全。
请注意,您可以使用任何对象作为锁:Object lock = new Object();
synchronized(lock){
//do something
}

于 2012-08-19T19:36:39.663 回答