同步方法和块的行为是不同的

时间:2016-12-22 12:56:41

标签: java multithreading synchronization synchronized-block

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

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方法中使用synchronized块时,输出的同步如下:

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

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

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

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

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

1 个答案:

答案 0 :(得分:1)

对于整个方法的长度,同步方法等同于synchronized(this) - 块,但是您的代码使用synchronized(target)target是{{1}的共享实例}}。换句话说:正在同步的对象是不同的,因此行为不一样。

在使用Callme的情况下,这意味着所有线程在同一synchronized(target)实例上同步,因此它们的行为是串行的:一个线程将保存该Callme实例的监视器对于Callme方法的整个持续时间,所以实际上线程是一个接一个地执行的。

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

其他一些评论:

  • 调用System.out是一个坏主意,因为它会自行等待
  • 一般情况下,不要在Thread.currentThread().join()实现中创建和启动Thread实例,该实例将由该线程运行:您将失去对该线程的访问权限。特别是不要在构造函数中执行此操作,因为您要将部分构造的对象发布到Runnable,这在此代码中不是一个大问题,但可能会在更复杂的应用程序中导致细微的错误。 / LI>