为什么将lock对象设为私有封装锁,以便客户端代码无法获取它?
对象的内在锁定
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);
}
}
}
答案 0 :(得分:3)
好吧,另一个类根本无法访问set
,因为它是私有的。在其他人无法做到的许多事情中,因为他们无法访问该引用,因此同步它就是一个。
如果getter直接返回该引用(没有包装或复制对象),则其他人可以在其上进行同步,从而破坏了对私有对象的锁定的好处。
答案 1 :(得分:1)
在您提供的两个示例中,都使用了内在监视器。但是,第二个示例使用set
字段的内部锁定。使用此练习时要小心:如果您发布对set
的引用,则类外的代码可能会在其监视器上同步,这可能导致死锁。
我认为您所获得的区别是synchronized
与使用java.util.concurrent.Lock
API相比。您获得的大部分内容都增加了灵活性(来自Lock文档):
同步方法或语句的使用提供了对与每个对象关联的隐式监视器锁的访问,但强制所有锁获取和释放以块结构方式发生:当获取多个锁时,它们必须在相反的情况下释放订单,所有锁必须在获得它们的相同词汇范围内发布。
虽然同步方法和语句的作用域机制使得使用监视器锁进行编程变得更加容易,并且有助于避免许多涉及锁的常见编程错误,但有时您需要以更灵活的方式使用锁。例如,一些用于遍历并发访问的数据结构的算法需要使用“手动”或“链锁定”:获取节点A的锁,然后获取节点B,然后释放A并获取C,然后释放B并获得D等。 Lock接口的实现允许使用这些技术,允许在不同的范围内获取和释放锁,并允许以任何顺序获取和释放多个锁。
还有两个方法调用,它们为您提供了使用Lock API获取锁定的更多方法:
lockInterruptibly
:除非线程被中断,否则获取锁定。tryLock
:只有在调用时可用时才获取锁(可选超时)。Java中并发的规范参考是'Java Concurrency in Practice'。
答案 2 :(得分:0)
为什么将lock对象私有化封装了这个锁 客户端代码无法获取它?
说实话,我不知道这个问题的含义是什么
您的代码段之间的区别在于使用的锁。在第一种情况下,整个方法被同步(使用A
的内在锁),而在第二种情况下,您锁定了您尝试修改的集合。即2个线程可以同时进入该方法,但1将阻止尝试锁定add
这种方法有两个优点: 1)它定义了锁定的粒度(对于需要锁定很短时间的长方法,锁定整个持续时间通过锁定集合使线程安全,方法调用会降低性能)和 2)。
请注意,您可以将任何对象用作锁:Object lock = new Object();
synchronized(lock){
//do something
}