Java Synchronized 将 class 的所有同步方法彼此同步?

Java Synchronized synchronizes all synchronized methods of a class among each other?

我对 java 中的同步有疑问。在下面的 Java 程序中,我没有得到任何输出。但是,如果我从方法 IFoo.s() 中删除同步语句,我将得到一些输出。 IFoo.setP() 和 IFoo.s() 方法似乎彼此同步。但是'synchronized'应该只是防止两个线程同时调用synchronized方法吧?

package com.example.relectiontest;

import java.awt.Point;
import java.util.Random;

public class Main {

public static void main(String[] args) throws Exception{
    final IFoo f = new IFoo();
    Runnable r = new Runnable() {
        public void run() {
            Random r = new Random();
            int a = r.nextInt(5)+1;
            for(int i=0;i<1000000;++i){
                f.setP(a);
            }
        }
    };
    Runnable r2 = new Runnable() {
        public void run() {
            for(int i=0;i<1000000;++i){
                f.s();
            }
        }
    };
    Thread T1 = new Thread(r, "T1");
    Thread T2 = new Thread(r, "T2");
    Thread T3 = new Thread(r2, "T3");
    T3.start();
    T1.start();
    T2.start();
}

private static class IFoo{
    private Point p = new Point();

    public synchronized void setP(int a){
        //System.out.println("p1 "+Thread.currentThread());
        p.x = a;
        p.y = p.x;
        int x = p.x , y = p.y;
        if(x != y)
            System.out.println(Thread.currentThread()+"\t"+x+" "+y);
        //System.out.println("p2 "+Thread.currentThread());
    }

    public synchronized void s(){
        //System.out.println("s");
        p.x = 0;
    }
}
}

那么,为什么我看不到任何输出?

问候

来自documentation

making these 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.

因为通常你不应该得到任何输出,因为 x 应该等于 y。 但是,当您删除 synchronized 关键字时,两个线程同时执行,如果 s() 在 p.y=p.x 和 x=p.x 语句之间执行,您可能会得到一个输出。

因为同步 x != y 永远不会是真的。

在您的未同步版本中,s() 有机会每隔一段时间将 p.x 设置为 0(即使未正确同步)。

在同步版本中 s() 必须等到 setP 完成(因为它们都是同步的,共享隐式 this 锁),并且由于 this 中的逻辑=14=] 条件不成立。

您的示例过于复杂。可以这样写出来(在两种方法上都加上synchronized,看什么都不打印):

private static class IFoo {
    volatile int x = 0;
    public void setP(int a) {
        x = a;
        if(x != a)
            System.out.println("Someone changed x!");
    }

    public void s() {
        x = 0;
    }
}

另请注意,静态同步方法在 Class 对象上进行同步,因为它们没有 this。因此,实例和静态方法不会相互锁定,除非您在公共锁上显式同步。

只有在以下情况下才会显示输出:

 if(x != y) 

因为在行中:

 p.x = a;
 p.y = p.x;
 int x = p.x , y = p.y;

你使 x == y 不显示输出。

当您从 s 方法中删除同步关键字时 - 线程有时会将 x 设置为 0,这会使 if(x != y) - 为真。并且输出是可见的。

在 Java 中,所有 synchronized 调用都在一个对象上同步。对于实例方法,它们的对象是 class 实例 - 因此在您的情况下 setPs 都在 IFoo.

的实例上同步

这允许您控制对通过多种方法访问的共享字段的访问。对于您的代码,这正是您所需要的——您需要确保 setP 中没有一个线程正在更改状态,而 s 中的另一个线程正在读取状态。

如果您喜欢更精细的控制,您可以使用同步块,它允许您指定要锁定的对象:

private final Object o=new Object();

public void method(){
    synchronized (o){
        //Synchronized code
    }
}

这是通常的 generally recommended approach - 它允许您封装您的锁,因此您不会冒其他 class 干扰您的锁并可能对您的代码进行 DOS 的风险。

静态方法在 class 对象(例如 IFoo.class)上同步。