简体   繁体   English

java:可以同步块交错吗?

[英]java: can synchronized block interleave?

I have an unexpected (for me at least) output with this code 使用此代码,我有一个意外的(至少对我来说)输出

public class Test {
    static boolean condition = false;

    void runme() {
        var reader = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("waiting for condition");
                    while (!condition) {}
                    System.out.println("condition is true");
                }
            }
        };

        var writer = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("condition is set to true");
                    condition = true;
                }
            }
        };

        new Thread(reader).start();
        new Thread(writer).start();

    }

    public static void main(String[] args) {
        new Test().runme();
    }
}

Based on the documentation, I expected a deadlock if the reader object starts first, since 根据文档,如果reader对象首先启动,我预计会出现死锁

  1. it acquires the lock for this (entering the synchronized block) this获取锁(进入同步块)
  2. prints "waiting for condition" 打印“等待条件”
  3. get stuck in the infinite loop forever 永远陷入无限循环
  4. the other thread waits for the this lock, to get into its own synchronized block 另一个线程等待this锁,进入它自己的同步块

However, on some runs of the code I get the output 但是,在某些代码运行中,我得到了输出

waiting for condition
condition is set to true
condition is true

Am I missing something or have I misunderstood how synchronized blocks/methods work? 我错过了什么或者我误解了同步块/方法是如何工作的?

The two synchronized (this) statements reference the Runnable anonymous classes. 两个synchronized (this)语句引用Runnable匿名类。
So the synchronizations of the two Runnable instances don't operate on the same lock. 因此,两个Runnable实例的同步不会在同一个锁上运行。
You have to synchronize on the outer class instance to lock on the same monitor such as : 您必须在外部类实例上进行同步才能锁定同一监视器,例如:

synchronized (Test.this) {...}

Additionally, note that by using a lambda to implement the Runnable functional interface such as : 另外,请注意通过使用lambda来实现Runnable功能接口,例如:

var writer = () -> {
    synchronized (this) {
        System.out.println("condition is set to true");
        condition = true;
    }
};

you could keep the actual syntax ( synchronized (this) ) as this in this case doesn't refer the anonymous class that doesn't exist but refers the outer instance. 你可以保持实际语法( synchronized (this) )作为this在这种情况下并不是指不存在,而是指外实例的匿名类。

In your code, synchronized(this) refers to two different objects. 在您的代码中, synchronized(this)引用两个不同的对象。 So neither code blocks the other, they just run simultaneously. 所以两个代码都不会阻塞另一个,它们只是同时运行。

Another way to look at it is to not use var or local classes. 另一种看待它的方法是不使用var或local类。 Just declare two top level classes that do the same thing as reader and writer : 只需声明两个与readerwriter完全相同的顶级类:

//       var reader = new Runnable() {
class Reader implements Runnable {
        @Override
        public void run() {
            synchronized (this) {
                System.out.println("waiting for condition");
                while (!condition) {}
                System.out.println("condition is true");
            }
        }
    }


//        var writer = new Runnable() {
class Writer implements Runnable {
        @Override
        public void run() {
            synchronized (this) {
                System.out.println("condition is set to true");
                condition = true;
            }
        }
    }

It should be pretty obvious that this in this case refers to an instance of each class, not the same object, so these two classes could never block each other. 它应该是很明显, this在这种情况下指的是每个类,而不是同一对象的一个实例,因此这两类永远无法阻止对方。

The reference this refers to the current object. 引用this是指当前对象。 Inside the instance method of an inner class this refers to the current object of the inner class. 在内部类的实例方法中, this指的是内部类的当前对象。 In case you want to access the current object of outer enclosing class then you need to use like : OuterClassName.this . 如果你想访问外部封闭类的当前对象,那么你需要使用: OuterClassName.this

In your case you two separate objects of have anonymous classes. 在您的情况下,您有两个具有匿名类的单独对象。 The synchronized blocks do not share common object to lock upon so actually there is no synchronization rather both the threads run in parallel independently. 同步块不共享要锁定的公共对象,因此实际上没有同步,而是两个线程独立并行运行。

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

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