繁体   English   中英

有关同步方法,锁和监视器的说明

[英]Clarifications about synchronized methods, locks and monitor

为了加深我对Java同步的了解,我的困惑始于以下声明,取自此处

Java中的每个对象都与一个监视器关联,线程可以锁定或解锁监视器。 [...]如果完成了[synchronized]方法主体的执行,无论是正常执行还是突然执行,都会在同一监视器上自动执行解锁操作。

因此,我正在想象一个像圆顶的监视器,它覆盖整个对象,并防止两个线程同时访问它。 更清楚地说,我认为

class MySync{
    synchronized void foo(){}
    void bar(){}
    String x;
}

如果threadA访问并运行(同步) foo()方法5秒钟,它将“激活圆顶”,从而阻止threadB访问对象的任何其他成员,如barx

我编写这些代码行只是为了进行一些测试,然后发现我错了……

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() ,则会得到我期望的行为。 这是否意味着我的假想圆顶仅一次覆盖所有同步方法(即,所有同步方法都使用一个锁)?

同步是一种开发人员工具,用于使线程之间能够协调地访问资源。

Java语言规范规定

synchronized语句代表执行线程获取互斥锁(第17.1节),执行一个块,然后释放该锁。 当执行线程拥有该锁时,其他任何线程都无法获取该锁。

在您的情况下,线程A获取锁并执行foo (已实现为需要锁)。 然后,线程B执行尚未实现以要求锁定的bar 因此, B未被阻止。

再次从规格

获取与对象相关联的锁本身并不能防止其他线程访问该对象的字段或调用该对象上的未同步方法。 其他线程也可以以常规方式使用同步方法或同步语句来实现互斥。

同步看作访问的东西必须通过同步目标来完成双方之间的协议。 如果某些代码忽略这个协议,并直接访问的东西 ,它可能会导致在提到线程干扰和内存一致性错误的同步Java教程

是的,虚构的球顶仅覆盖该对象的所有同步方法,以及该对象上显式同步的代码段,例如:

,,,
synchronize(this) {
doSomething();
}

并非代码的所有部分都是线程安全敏感的,因此某些方法可以在同一对象中“不同步”,从而加快了执行速度。

“ Java中的每个对象都与一个监视器关联是什么意思?”。

这意味着您在每个对象上都有可用的监视器。 您可以通过在对象上声明同步方法或使用syncnize(obj)方法来访问它。 因此,您无需引入另一个提供监视器功能的“实体”。 对于适当的监视器,您可以访问每个对象的wait()和notify()方法。

syncrhonize(obj) {
 while(someConditionIsNotTrue) {
 obj.wait();
 }
}

因此,只要您的代码逻辑需要对对象的独占访问权,就可以使用内置监视器并对其调用同步。

为了提供线程安全,所有零件都必须按照相同的规则进行游戏,并在需要时同步使用。 为了避免由于遗漏而导致的错误,存在更高级别的并发机制,例如AtomicInteger和ConcurrentCollections。

为了处理这种圆顶机制,每个Java对象都有一个类型为lock flag ,并且使用synchronized可以根据以下模式与该lock标志进行交互。

同步之前锁定标志和对象

调用同步(此)后,线程将保留锁定标志,直到线程完成处理或经历某些特定的中断情况为止。

线程获取的锁定标志

虽然锁标志由第一线程获得,如果第二个线程来了,尝试访问由引用的对象 this是.won't能够因为锁标志是不存在的。 因此,线程调度程序会将新线程放入等待对象锁定标志的线程池中。 它会一直停留到释放锁定标志为止,并且可能会立即访问该对象,也可能不会立即访问该对象,具体取决于线程调度程序的调度决定

等待池

发生以下事件时,将释放锁定标志

  • 线程通过同步代码块的末尾
  • 同步代码块引发中断,返回或异常时

为了正确使用同步,务必确保

  • 对微妙数据的所有访问均已synchronized (您应该已完成)
  • 所有受同步保护的微妙数据必须是私有的

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM