0

我观察到使用同步方法或同步块产生不同结果的场景。从下面的代码:

class Callme {
    void call(String msg) {
        System.out.print("[" + msg);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("]");
    }       
}

class Caller implements Runnable{
    String msg;
    Callme target;
    Thread t;

    public Caller(Callme target, String msg) {
        this.target = target;
        this.msg = msg;
        t = new Thread(this, "Caller thread");
        t.start();
    }

    @Override
    public void run() {
        synchronized(target) {
            target.call(msg);
            new Callme().call(msg);
        }
    }

}

public class Test {

    public static void main(String[] args) throws InterruptedException {
        Callme obj = new Callme();

        new Caller(obj, "thread1");
        new Caller(obj, "thread2");
        new Caller(obj, "thread3");

        Thread.currentThread().join();
    }
}

当我在 Caller::run 方法中使用同步块时,输出同步如下:

[thread1]
[thread1]
[thread3]
[thread3]
[thread2]
[thread2]

但是当我对 Callme::call 方法使用同步方法时,而不是同步块,输出不同步:

[thread1]
[thread1[thread2]
]
[thread3[thread2]
]
[thread3]

我的期望是两种情况下的输出都不应该同步,因为我在调用“Callme::call”方法时使用了不同的对象

这让我质疑我对同步块概念的理解?

4

1 回答 1

1

synchronized(this)对于整个方法的长度,同步方法等效于-block,但是您的代码使用的是synchronized(target),并且target是 的共享实例Callme。换句话说:被同步的对象不同,所以行为也不一样。

在您使用 的情况下synchronized(target),这意味着所有线程在同一个实例上同步,因此它们的行为是串行的:线程将在方法的整个持续时间内Callme持有该实例的监视器,因此实际上线程是在其他。CallmeCaller.run

在同步方法的情况下,每个线程都在它们自己的实例上同步Caller,因此实际上没有序列化(对 的写入除外System.out)。

一些补充说明:

  • 调用Thread.currentThread().join()是个坏主意,因为它会等待自己
  • 通常,不要在将由该线程运行Thread的实现中创建和启动实例:您将无法访问该线程。Runnable特别是不要在构造函数中这样做,因为您将部分构造的对象发布到Thread,这在这段代码中不是一个大问题,但可能会导致更复杂的应用程序中的细微错误。
于 2016-12-22T13:01:44.227 回答