静态和非静态同步,为什么输出结果不同?

Static and non-static synchronization, why the output results differ?

为什么输出有差异?我有 2 个案例:

  1. 在第一种情况下,我使用静态函数 f1f2
    public static synchronized void f1() {
        for (int i = 0; i < 100; i++)
            System.out.print("A");
    }
    
    public static synchronized void f2() {
        for (int i = 0; i < 100; i++)
            System.out.print("B");
    }
    
    这是我的主要方法体:
    Thread t1 = new Thread(
            new Runnable(){public void run(){f1();}}
    );
    
    Thread t2 = new Thread(
            new Runnable(){public void run(){f2();}}
    );
    t1.start();
    t2.start();
    
    输出是:
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
    
  2. 在第二种情况下 f1f2 不是静态的:
    public synchronized void f1() {
        for (int i = 0; i < 100; i++)
            System.out.print("A");
    }
    
    public synchronized void f2() {
        for (int i = 0; i < 100; i++)
            System.out.print("B");
    }
    
    输出是如此混乱:
    AAABABABBBBAAAAAAAAAABBAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBAA
    

static 也在 class 应用了一个锁(JLS-8.4.3.6. synchronized Methods 说,部分地, 对于 class (static ) 方法,使用与方法的 class 的 Class 对象关联的监视器。对于实例方法,与 this(调用该方法的对象)关联的监视器是使用 )。在您的情况下,您可以从方法中删除 static 并从 System.out 中删除 synchronize。像

public void f1() {
    synchronized (System.out) {
        for (int i = 0; i < 100; i++) {
            System.out.print("A");
        }
    }
}   

public void f2() {
    synchronized (System.out) {
        for (int i = 0; i < 100; i++) {
            System.out.print("B");
        }
    }
}   

这将强制线程在写入之前获取 System.out 上的锁。

静态同步函数在 class 上同步,而非静态同步函数在实例上同步。

这意味着当函数是静态的时,它们会相互阻塞,而当它们不是(并使用不同的实例)时,它们不会。

如果您的 class 被称为 MyClass 那么:

public static synchronized foo() {...}

类似于:

public static foo() {
    synchronized (MyClass.class) {
        // ...
    }
}

同时:

public synchronized foo() {...}

类似于:

public foo() {
    synchronized (this) {
        // ...
    }
}

通常,您希望指定要同步的对象(您希望独占哪些资源?)并避免在 class/instance 上使用隐式同步,正是因为这个原因。