简体   繁体   English

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

[英]Clarifications about synchronized methods, locks and monitor

Trying to deepen my knowledge of synchronization in Java, my confusion started from the following statement, taken from here : 为了加深我对Java同步的了解,我的困惑始于以下声明,取自此处

Each object in Java is associated with a monitor, which a thread can lock or unlock. Java中的每个对象都与一个监视器关联,线程可以锁定或解锁监视器。 [...] If execution of the [synchronized] method's body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor. [...]如果完成了[synchronized]方法主体的执行,无论是正常执行还是突然执行,都会在同一监视器上自动执行解锁操作。

So, I was imagining a monitor like a dome, covering the whole object, and preventing two threads to access it in the same time. 因此,我正在想象一个像圆顶的监视器,它覆盖整个对象,并防止两个线程同时访问它。 To be more clear, I thought that, given 更清楚地说,我认为

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

if threadA accesses and run (synchronized) foo() method for 5 seconds, it "activates the dome" preventing threadB to access any other member of the object like bar or x . 如果threadA访问并运行(同步) foo()方法5秒钟,它将“激活圆顶”,从而阻止threadB访问对象的任何其他成员,如barx

I wrote these lines of code just to make some test, and I figured out that I was wrong... 我编写这些代码行只是为了进行一些测试,然后发现我错了……

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");
    }
}

This is the output: 这是输出:

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

My questions is: 我的问题是:

What does it mean that each object in Java is associated with a monitor ? Java中的每个对象都与一个监视器关联是什么意思? Isn't that a bit ambiguous? 那不是有点模棱两可吗? If I declare synchronized bar() too, I get the behaviour I was expecting. 如果我也声明了同步的bar() ,则会得到我期望的行为。 Does it mean that my imaginary dome covers only ALL synchronized methods at once (ie one lock for all the sync methods)? 这是否意味着我的假想圆顶仅一次覆盖所有同步方法(即,所有同步方法都使用一个锁)?

Synchronization is a developer tool you use to enable coordinated access to a resource between threads. 同步是一种开发人员工具,用于使线程之间能够协调地访问资源。

The Java Language Specification states Java语言规范规定

A synchronized statement acquires a mutual-exclusion lock (§17.1) on behalf of the executing thread, executes a block, then releases the lock. synchronized语句代表执行线程获取互斥锁(第17.1节),执行一个块,然后释放该锁。 While the executing thread owns the lock, no other thread may acquire the lock. 当执行线程拥有该锁时,其他任何线程都无法获取该锁。

In your case, thread A acquires the lock and executes foo which has been implemented to require the lock. 在您的情况下,线程A获取锁并执行foo (已实现为需要锁)。 Then thread B executes bar which hasn't been implemented to require the lock. 然后,线程B执行尚未实现以要求锁定的bar B therefore isn't blocked. 因此, B未被阻止。

Again from the specification 再次从规格

Acquiring the lock associated with an object does not in itself prevent other threads from accessing fields of the object or invoking un-synchronized methods on the object. 获取与对象相关联的锁本身并不能防止其他线程访问该对象的字段或调用该对象上的未同步方法。 Other threads can also use synchronized methods or the synchronized statement in a conventional manner to achieve mutual exclusion. 其他线程也可以以常规方式使用同步方法或同步语句来实现互斥。

Think of synchronization as an agreement between parties that access to something has to be done through the synchronization target. 同步看作访问的东西必须通过同步目标来完成双方之间的协议。 If some code ignores this agreement and accesses that something directly, it may cause the thread interference and memory consistency errors mentioned in the Java tutorials on Synchronization . 如果某些代码忽略这个协议,并直接访问的东西 ,它可能会导致在提到线程干扰和内存一致性错误的同步Java教程

Yes, the imaginary dome covers only all the synchronized methods of that object and the pieces of code explicit synchronized on that object like: 是的,虚构的球顶仅覆盖该对象的所有同步方法,以及该对象上显式同步的代码段,例如:

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

Not all parts of code are thread-safety sensitive, so some methods can be 'unsynchronized' in the same object, which speeds up the execution. 并非代码的所有部分都是线程安全敏感的,因此某些方法可以在同一对象中“不同步”,从而加快了执行速度。

"What does it mean that each object in Java is associated with a monitor?". “ Java中的每个对象都与一个监视器关联是什么意思?”。

It means just that you have the monitor available on each object. 这意味着您在每个对象上都有可用的监视器。 And you can access it by declaring synchronized method on the object, or by using synchronize(obj) method. 您可以通过在对象上声明同步方法或使用syncnize(obj)方法来访问它。 So you don't need to introduce another 'entity' which provides monitor functionality. 因此,您无需引入另一个提供监视器功能的“实体”。 And as for proper monitor you have access to wait() and notify() methods on each object. 对于适当的监视器,您可以访问每个对象的wait()和notify()方法。

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

So whenever logic of your code needs exclusive access to the object you can use the built-in monitor and call synchronize on it. 因此,只要您的代码逻辑需要对对象的独占访问权,就可以使用内置监视器并对其调用同步。

To provide thread safety, all the parts have to play according to the same rules and use synchronized when needed. 为了提供线程安全,所有零件都必须按照相同的规则进行游戏,并在需要时同步使用。 To prevent bugs due to the omissions there are higher-level concurrent mechanism, like AtomicInteger, and ConcurrentCollections. 为了避免由于遗漏而导致的错误,存在更高级别的并发机制,例如AtomicInteger和ConcurrentCollections。

In order to handle this dome mechanism there exist for every Java object a flag of type lock flag , and using synchronized allows interaction with that lock flag according to the following pattern. 为了处理这种圆顶机制,每个Java对象都有一个类型为lock flag ,并且使用synchronized可以根据以下模式与该lock标志进行交互。

同步之前锁定标志和对象

After synchronized (this) is invoked the lock flag will be held by the thread until it finishes its processing or it goes through some specific cases of interruption. 调用同步(此)后,线程将保留锁定标志,直到线程完成处理或经历某些特定的中断情况为止。

线程获取的锁定标志

While the lock flag is acquired by a first thread, if a second thread comes and try to access the object referenced by this it .won't be able because the lock flag is absent. 虽然锁标志由第一线程获得,如果第二个线程来了,尝试访问由引用的对象 this是.won't能够因为锁标志是不存在的。 The threading scheduler will therefore put that new thread in the pool of the threads waiting for the object's lock flag. 因此,线程调度程序会将新线程放入等待对象锁定标志的线程池中。 It will stay there until the release of the lock flag and may have immediate access or may not have immediate access to the object depending on the scheduling decision of the thread scheduler 它会一直停留到释放锁定标志为止,并且可能会立即访问该对象,也可能不会立即访问该对象,具体取决于线程调度程序的调度决定

等待池

The lock flag will be released when the following events occur 发生以下事件时,将释放锁定标志

  • The thread passes the end of the synchronized code block 线程通过同步代码块的末尾
  • When a break, a return, or an exception is thrown by the synchronized code block 同步代码块引发中断,返回或异常时

In order to properly use synchronize it is very important to always ensure that 为了正确使用同步,务必确保

  • All accesses to delicate data are synchronized (which you should have done) 对微妙数据的所有访问均已synchronized (您应该已完成)
  • All the delicate datas protected by synchronized must be private 所有受同步保护的微妙数据必须是私有的

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

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