同步方法和块的行为不同



i观察到一种情况,其中使用同步方法或同步块产生不同的结果。 从下面的代码:

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

当我在呼叫者中使用同步块::运行方法时,OUPUT的同步如下:

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

但是,当我使用callme :: Call方法的同步方法时,而不是同步块时,输出不会同步:

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

我的期望是在两种情况下都不应同步输出,因为我在调用" callme :: call"方法

时使用了不同的对象

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

同步方法等于整个方法的长度的synchronized(this) -Block,但是您的代码使用synchronized(target),而targetCallme的共享实例。换句话说:要同步的对象是不同的,因此行为不相同。

在使用synchronized(target)的情况下,这意味着所有线程在Callme的同一实例上同步,因此它们的行为是串行的:线程将在Caller.run方法的整个过程中保存该Callme实例的监视器,因此线程被一个接一个地执行。

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

一些其他评论:

  • 呼叫Thread.currentThread().join()是一个坏主意,因为它会等待自己
  • 一般而言,不要在Runnable实现中创建并启动Thread实例,该实施将由该线程运行:您将失去对线程的访问。特别是不要在构造函数中执行此操作,因为您正在向Thread发布部分构造的对象,这在此代码中不是一个大问题,但可能会导致更复杂的应用程序中的微妙错误。

最新更新