2

我有一个关于java中同步的问题。在下面的Java程序中,我没有得到任何输出。但是,如果我从方法IFoo.s()中删除同步语句,我会得到一些输出。看起来IFoo.setP()和IFoo.s()方法是相互同步的。但'synchronized'只能防止两个线程同时调用synchronized方法,对吗?Java 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; 
    } 
} 
} 

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

问候

+0

你知道你的'System.out'调用在'IFoo.s()'中被注释掉了吗? –

+2

'synchronized'实例方法将所有*一起同步到同一个实例*。 – Holger

回答

3

在Java中,所有​​调用都在一个对象上同步。例如方法,他们反对的是类实例 - 所以在你的情况下,setPs都在IFoo的实例上同步。

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

如果您需要更细的控制,你可以使用synchronized块,它允许你指定的对象锁定在:

private final Object o=new Object(); 

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

这是一般generally recommended approach - 它允许你封装你的锁,所以你不不要冒险让其他一些组织干扰你的锁定,并且可能会对你的代码执行代码。

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

1

documentation

使得这些方法同步有两个作用:

首先,这是不可能的同一对象的同步方法交错两个调用。当一个线程正在执行对象的同步方法 时,所有其他线程将为同一个对象块(暂停执行) 调用 同步方法,直到第一个线程完成对象。其次,当一个同步方法退出时,它会自动建立与同一对象的任何后续调用同步方法的before-before关系。此 保证对所有 线程都可见对象状态的更改。

0

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

4

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

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

在同步s()版本必须等到setP完成(因为他们都同步,共享隐含this锁),并且由于在setP逻辑的条件不可能是真实的。

您的示例过于复杂。你可以把它写出来如下(在这两种方法中添加同步地看到,什么都不会被打印):

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为不显示输出

1

只有当输出将被显示出来。

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