[英]java sychronization with object lock confusion
我已经阅读了很多关于Java对象级同步的教程。 而我所理解的是,每当线程调用同步方法/块时,它将获取方法对象的锁定并在执行后释放。 因此,在锁定时,没有其他线程可以访问相同的对象数据。 但是当我尝试下面的场景时,发现其他线程可以访问并行锁定对象的非同步方法。
class A {
public synchronized void test1(){}
public void test2(){}
}
我创建了3个线程t1
, t2
和t3
。 并且所有都引用了类A的相同对象.t1正在调用test1()
和其他两个调用test2()
线程。 在这里我可以看到,当test1()
正在执行时, t2
和t3
线程test2()
也并行运行。
所以我的问题是同步是否真的完全锁定了一个对象,或者它只锁定了特定的同步方法? 如果是这样,为什么它调用对象级锁定。
在类级别锁定的情况下会发生什么?
同步确保任何需要在同步块中执行代码块的线程首先必须在块内部的代码执行开始之前获取Object的监视器( 锁定 )。 因此,当一个线程在同步块内执行代码时,尝试执行相同或其他同步块的其他线程(在同一对象上,即相同的锁 )将不得不等到它们获得锁定。
允许其他线程在未同步的代码块中执行代码,因为这不涉及获取监视器/锁定。 请注意,如果需要获取不同的锁,其他线程甚至可以执行同步块中的代码。
如果其他线程尝试执行任何其他已同步的方法,则需要获取锁定。 他们可以自由地调用其他没有synchronized关键字的方法(即使锁被一些线程获取),因为调用它们不需要获取lock / moniter。
显式锁定对象(例如A
的实例)与A
类中声明的内部锁定习语之间存在差异。
在您的情况下,方法test1
是实例同步的,这意味着对于给定的A
实例,当时只有一个线程可以调用它。
但是, test2
不同步,因此任何给定时间的任何线程都可以在理论上调用它。
现在,如果您synchronized(myAInstance)
,那么一旦获得锁定,只有synchronized
语句中的执行代码才能调用A
实例的任何方法,直到锁定被释放。
你真的搞混了什么。
class A {
public synchronized void test1(){}
public void test2(){}
}
这将创建对test1方法的同步访问。 这意味着,只有一个并行线程可以执行该方法。 如果你想拥有一个锁定方法,你可以创建自己的锁对象:
class A {
private static final Object myLock= new Object();
public void test1(){
synchronized (myLock){
}
}
public void test2(){
synchronized (myLock){
}
}
}
如果应用程序中的任何其他人使用相同的对象,则您的线程也会被阻止。 通常你定义一种每个人都遵循的锁对象结构。
虽然在许多情况下将同步视为排除手段可能是合适的,但还有另一种方法可以将其视为对其他人更具说明性,这就是可见性的概念。
退出同步块意味着所有更改都将提交回内存。 输入同步块可确保您可以正确地看到其他人可能对您要访问的内存所做的所有更改。
如果你认为同步是一种在你做东西的时候锁定别人的方法,你需要把它想象成一个“被占用” - 而不是门锁:只有其他人礼貌你才能独自一人 - 也就是说,如果他们也使用相同的同步机制。 考虑可见性使得更容易理解为什么有礼貌才能得到回报。
当然,在你的情况下, test1
和test2
方法都必须标记为'synchronized'。 这与将A对象用作锁定相同。
更进一步,我真的很想鼓励你看一下java.util.concurrent
-package及其子包atomic
和locks
。 您想要实现的大部分内容已经在那里实现,使用这些库可以让您更轻松,更安全。 它还使您的代码更易于维护和更易于阅读。
阅读有关同步方法的 oracle文档页面
使这些方法
synchronized
有两个影响:首先,对同一对象的两个
synchronized
方法的调用不可能进行交错。 当一个线程正在为对象执行synchronized
方法时,所有其他线程调用同一对象的synchronized
方法(暂停执行)直到第一个线程完成对象。
=>在任何时候,您都不能并行地在同一对象上执行两个synchronized
方法。
回到您的查询:
test1()
是synchronized
, test2()
不是。
T1 execution of test1() and T2 & T3 exection of test2() does not break above rule
,因此它们可以并行运行。
如果将test2()
更改为synchronized
方法,事情就会发生变化。
在这种情况下,直到T1
完成它的执行test1()
, T2 and T3
必须等到T1
完成它的执行。 一旦T1
完成执行, T2 or T3
可以调用test2()
方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.