Java: synchronize 关键字不会阻塞不同线程上的对象

Java: synchronize keyword doesn't block object on different thread

public class SynchronizeTest {

    public synchronized void methodA() {
        System.out.println("calling method a ...");
        sleep(2000);
        System.out.println("ending method a");
    }

    public void methodC() {
        System.out.println("calling method C");
        new Thread(new Runnable() {
            @Override
            public void run() {
                methodA();
            }
        }).start();
        sleep(100);
        System.out.println("end method C");
    }

    public static void main(String[] args) {
        new SynchronizeTest().methodC();
    }


    static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

起初,我猜是因为我 synchronize 方法 A,这意味着整个对象将被锁定,直到此方法完成。所以答案应该是:

calling method C
calling method a
ending method a
ending method C

但结果是这样的:

calling method C
calling method A
ending method C
ending method A

这意味着 methodA 没有像我猜测的那样锁定对象。请告诉我为什么。

关键字synchronized的意思是,不同时发生的错误 线程使用相同的变量,被阻止。 锁定一个线程根本没有意义,因为你可以调用一个方法来代替。

首先,您在这里创建了 2 个线程 1) 主线程和 2)(比如说线程 1)。现在发生的事情是你让 methodA 同步所以当 thread1 到达那里时它将保留在那个方法中并尝试完成执行但同时主线程可以进一步处理并打印最后一个 C statement.because 你在你的方法中添加了睡眠调用这会导致 thread1 休眠 2 秒。所以输出将是 C(main thread) , a(your thread) , C(main thread) , a(your thread)

当您在实例方法上使用 synchronized 关键字时,意味着该对象上的方法当时只能由一个线程调用。

这并不意味着对所有对象的方法进行任何形式的锁定,即在该方法的调用期间可以调用其他实例方法 - 当您在执行时异步调用它时也更容易。

由于您在 methodC 中的线程中调用它,并且 methodA 中的休眠时间比终止前的休眠时间长得多 methodC,您当前的输出非常每次都有可能发生。

这是相关的文档引用(参见第 here 页):

[...]making [...] methods synchronized has two effects:

  • First, it is not possible for two invocations of synchronized methods >on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

  • Second, when a synchronized method exits, it automatically establishes > a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

备注

您可能想要 join 调用 methodA 的线程,以确保其执行在打印 methodC 中的最后一条语句之前终止。

读这个:https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html 正如它所说,对同一对象的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程将阻塞(暂停执行),直到第一个线程完成该对象。这意味着该对象将被其他同步方法锁定。 同步 methodC 也不是一个好主意。它将为您提供以下输出:调用方法 C / 调用方法 a / 结束方法 a / 结束方法 C。 为了得到你想要的输出,你可以使用下面的代码:

public class SynchronizeTest implements Runnable{

    public synchronized void methodA() throws InterruptedException {
        System.out.println("calling method a ...");
        Thread.sleep(2000);
        System.out.println("ending method a");
        this.notify();
    }

    public synchronized void methodC() throws InterruptedException {
        System.out.println("calling method C");
        new Thread(new Runnable() {
            @Override
            public void run() {
                methodA();
            }
        }).start();
        this.wait();
        System.out.println("end method C");
    }

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

    @Override
    public void run() {
        // TODO Auto-generated method stub

    }
}