为了加深我对Java同步的了解,我的混淆起自以下声明,取自here:
Java中的每个对象都与一个线程可以锁定或解锁的监视器相关联。 [...]如果[synchronized]方法正文的执行正常或突然完成,则会在同一台监视器上自动执行解锁操作。
所以,我想象一个像圆顶一样的监视器,覆盖整个对象,并阻止两个线程同时访问它。为了更清楚,我认为,鉴于
class MySync{
synchronized void foo(){}
void bar(){}
String x;
}
如果threadA访问并运行(同步)foo()
方法5秒钟,它"激活圆顶"阻止threadB访问对象的任何其他成员,如bar
或x
。
我编写这些代码只是为了进行一些测试,我发现我错了......
public class Main{
public static void main(String args[]) throws InterruptedException{
new Main();
}
MySync ms = new MySync();
RunnableA r1=new RunnableA(ms);
RunnableB r2=new RunnableB(ms);
Main() throws InterruptedException{
r1.start();
Thread.sleep(1000);
r2.start();
}
}
class MySync{
synchronized void foo(String tab){
System.out.println(tab+Thread.currentThread().getName()+" in foo()");
if("A".equals(Thread.currentThread().getName())){
System.out.println(tab+Thread.currentThread().getName()+" Waiting 5 seconds");
try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
}
System.out.println(tab+Thread.currentThread().getName()+" out foo()");
}
void bar(String tab){
System.out.println(tab+Thread.currentThread().getName()+" in bar()");
System.out.println(tab+Thread.currentThread().getName()+" out bar()");
}
}
class RunnableA implements Runnable{
String tab="";
Thread t=new Thread(this);
MySync ms;
RunnableA(MySync ms){
this.ms=ms;
t.setName("A");
}
void start(){t.start();}
@Override
public void run(){
System.out.println(tab+"A Running ");
ms.foo(tab);
System.out.println(tab+"A End running");
}
}
class RunnableB implements Runnable{
String tab=" ";
Thread t=new Thread(this);
MySync ms;
RunnableB(MySync ms){
this.ms=ms;
t.setName("B");
}
void start(){t.start();}
@Override
public void run(){
System.out.println(tab+"B Running ");
ms.bar(tab);
System.out.println(tab+"B End running");
}
}
这是输出:
A Running
A in foo()
A Waiting 5 seconds //threadA stuck in syncronized foo() method
B Running //threadB accesses bar() method anyway
B in bar()
B out bar()
B End running
A out foo()
A End running
我的问题是:
Java中的每个对象与监视器相关联是什么意思?这有点模棱两可吗?如果我也声明同步bar()
,我会得到我期望的行为。这是否意味着我想象中的圆顶一次只覆盖所有同步方法(即所有同步方法都有一个锁)?
答案 0 :(得分:2)
Synchronization是一种开发人员工具,用于在线程之间启用对资源的协调访问。
synchronized
语句获取互斥锁(第17.1节) 代表执行线程,执行一个块,然后释放 锁。 当执行线程拥有锁时,没有其他线程可以 获得锁定。
在您的情况下,线程A
获取锁并执行已实现为需要锁定的foo
。然后线程B
执行尚未实现的bar
以要求锁定。因此B
未被阻止。
再次来自规范
获取与对象关联的锁定本身并不存在 阻止其他线程访问对象的字段或调用 对象上的非同步方法。其他线程也可以使用 常规中的同步方法或同步语句 实现互斥的方式。
将同步视为各方之间的协议,即必须通过同步目标访问某些内容。如果某些代码忽略此协议并直接访问某些,则可能会导致Java tutorials on Synchronization中提到的线程干扰和内存一致性错误。
答案 1 :(得分:1)
是的,假想的圆顶仅涵盖该对象的所有同步方法以及在该对象上显式同步的代码片段,如:
,,,
synchronize(this) {
doSomething();
}
并非代码的所有部分都是线程安全敏感的,因此某些方法可以在同一个对象中“不同步”,从而加快执行速度。
“Java中的每个对象与监视器相关联是什么意思?”。
这意味着您可以在每个对象上使用监视器。您可以通过在对象上声明同步方法或使用synchronize(obj)方法来访问它。因此,您不需要引入另一个提供监视功能的“实体”。 至于适当的监视器,您可以访问每个对象上的wait()和notify()方法。
syncrhonize(obj) {
while(someConditionIsNotTrue) {
obj.wait();
}
}
因此,只要代码的逻辑需要对对象的独占访问,您就可以使用内置监视器并在其上调用同步。
为了提供线程安全,所有部件必须按照相同的规则进行播放,并在需要时使用同步。为了防止由于遗漏而导致的错误,有更高级别的并发机制,如AtomicInteger和ConcurrentCollections。
答案 2 :(得分:1)
为了处理这种圆顶机制,每个Java对象都存在lock flag
类型的标志,并且使用synchronized
允许根据以下模式与该锁定标志进行交互。
在调用synchronized(this)之后,线程将保持锁定标志,直到它完成处理或者经历一些特定的中断情况。
虽然锁定标志是由第一个线程获取的,但如果第二个线程出现并尝试访问 this
引用的对象,那么它将无法使用,因为锁定标志缺席。因此,线程调度程序将把新线程放在线程池中,等待对象的锁定标志。它将保持在那里直到释放锁定标志并且可能立即访问或者可能无法立即访问该对象,具体取决于线程调度程序的调度决策
发生以下事件时将释放锁定标志
为了正确使用同步,始终确保
非常重要synchronized
(您应该这样做)