为了正确理解Java中的并发问题和解决方案,我正在阅读官方Java教程。在其中一个页面中,他们定义了内部锁定和同步 link。在这个页面中,他们说:
只要一个线程拥有一个内部锁,就没有其他线程可以 获得相同的锁。另一个线程会在尝试时阻塞 获得锁定。
另外,他们在 Locks In Synchronized Methods 一节中提到:
当线程调用synchronized方法时,它会自动获取 该方法的对象的内在锁定,并在该方法时将其释放 方法返回。即使返回发生,也会发生锁定释放 一个未被捕获的例外。
对我来说,这意味着一旦我从其中一个线程调用同步方法,我就会拥有该线程的内部锁定,因为
内部锁在同步的两个方面都起作用: 强制独占访问对象的状态并建立 在对可见性至关重要的关系之前发生。
另一个线程是否无法调用同一个类的另一个同步方法?如果是,则使同步方法的整个目的失败。不是吗?
答案 0 :(得分:7)
所以只是重复我上面的评论作为答案。内部锁定意味着您不必创建对象来同步您的方法。相比之下,您可以通过调用synchronized(myLock) {...}
来使用外部锁定。
这是本书Java concurrency in practice的摘录:“每个对象都有一个内置锁的事实只是一个方便,所以你不需要显式创建锁对象”
这本书还说:
对象的内部锁定之间没有固有的关系 和它的国家;对象的字段不需要被其内在的保护 锁,虽然这是一个完全有效的锁定约定使用 许多班级。获取与对象关联的锁不会 阻止其他线程访问该对象唯一的事情 获取锁定可防止任何其他线程正在进行获取 同样的锁。每个对象都有内置锁的事实就是这样 方便,因此您无需显式创建锁定对象。 [9] 由您来构建锁定协议或同步 允许您安全地访问共享状态并使用它们的策略 贯穿整个计划。
但在脚注中说:
[9]回想起来,这个设计决定可能是一个糟糕的决定:不是 只是它可能令人困惑,但它迫使JVM实现者做出 对象大小和锁定性能之间的权衡。
回答你的上一个问题:你将无法从另一个线程调用同步方法,但你可以继续从同一个线程进入(内部锁是可重入的)。所以你必须想象在这种情况下锁定来自不同调用者线程的序列化方法访问。
如果您使用不正确的锁定然后引入活体危险,那么是的它被击败了。这就是为什么你必须确保你的并发线程不会相互竞争太多。
作为Brian Goetz puts in this blog entry:
在调整应用程序的同步使用时,我们应该尝试 很难减少实际争用的数量,而不是简单地尝试 避免使用同步
答案 1 :(得分:5)
似乎你有一个误解(dunno,如果它导致错误的结论),没有人指出。无论如何,一个简短的回答:
内在锁:只要认为它,JVM中的每个对象都内部有一个锁。 synchronized
个关键字尝试获取目标对象的锁。无论何时synchronized (a) { doSomething; }
,实际发生的是
a
中的锁定doSomething
)a
我希望你知道
public synchronized void foo() {
doSomething;
}
在概念上与
相同public void foo() {
synchronized(this) {
doSomething;
}
}
好的,回到你的问题,最大的问题,imho,是:
对我来说,这意味着一旦我从其中一个线程调用同步方法,我就会拥有线程的内在锁定,因为......
错误 。当你调用同步方法时,你不抓住线程的锁定。
相反,主题将拥有对象的内在锁定,即"拥有"方法。
e.g。在thread1中,您调用了a.foo()
,并假设foo()
已同步。 thread1将获取对象a
的内在锁定。
同样,如果调用AClass.bar()
(并且bar
被同步并且是静态方法),则将获取AClass
Class对象的内在锁。
答案 2 :(得分:4)
锁定一次只能由一个线程保存。这并没有打败这个目的; 是的目的。
线程 mut ually ex 通过获取锁定或互斥锁来在关键部分中同时执行操作。这提供了有效的原子性一系列不同的操作,以便其他线程永远不会看到可能违反一致性保证的中间状态。
答案 3 :(得分:3)
是的,由于内在锁定,您无法在同一对象上调用其他同步方法。在对象级别,只有1个线程将获取它。
答案 4 :(得分:2)
我无法调用同一个类的另一个同步方法吗?如果是,则使同步方法的整个目的失败。不是吗?
没有。您无法在同一对象上调用其他synchronized
方法进行对象级锁定,也无法在同一个类上调用其他static sysnchronized
方法。
但它并没有破坏同步的目的。
如果您按照synchronized方法的其他文档页面进行操作:
制作这些方法
synchronized
有两个影响:
- 首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在为一个对象执行一个synchronized方法时,所有其他线程都会调用同一个对象的同步方法(暂停执行),直到第一个线程完成该对象为止。
- 其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立一个先发生关系。这可以保证对所有线程都可以看到对象状态的更改。
醇>
如果允许两个synchronized
方法并行运行。您必然会在共享数据上出现内存不一致错误。
另一方面,Lock提供了更好的替代synchronized
构造。
相关的SE问题:
答案 5 :(得分:1)
synchronized
方法是否属于同一个类并不重要,重要的是方法的调用者线程是否获取锁定,如果是,则允许输入关键部分因为锁是reentrant
。
如果不是这样,那么递归调用会导致死锁,
fn(){
synchronized(mutex){ // the current thread has already acquired the mutex
fn();
}
}
fn这里不会死锁,因为锁是重入的,即(已经获取锁的线程可以再次进入并重新设置临界区,只要它仍然被获取)。
答案 6 :(得分:0)
锁可以分为两类 - “可重入”和“不可重入”。 在Java“synchronized”中,接口Lock(类ReentrantLock)的基本实现,接口ReadWriteLock(类ReentrantReadWriteLock) - 是可重入的。 重入意味着 - 一个线程可以一次又一次地持有锁。