Java Synchronized会同步一个类的所有同步方法吗?

时间:2015-08-20 12:03:20

标签: java multithreading synchronization thread-safety synchronized

我在java中有关于同步的问题。在下面的Java程序中,我没有得到任何输出。但是,如果我从方法IFoo.s()中删除synchronized语句,我将获得一些输出。似乎方法IFoo.setP()和IFoo.s()在彼此之间是同步的。但是'同步'应该只防止两个线程同时调用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;
    }
}
}

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

问候

5 个答案:

答案 0 :(得分:4)

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

在您的非同步版本s()中,偶尔可以将p.x设置为0(即使它未正确同步)。

在同步版本s()中必须等到setP完成(因为它们都已同步,共享隐式this锁定),并且感谢{{1}中的逻辑条件不可能是真的。

你的例子过于复杂。您可以按如下方式编写它(在两种方法上添加同步以查看将不会打印任何内容):

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。因此,除非您明确地同步公共锁,否则实例和静态方法不会相互锁定。

答案 1 :(得分:3)

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

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

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

private final Object o=new Object();

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

这通常是generally recommended approach - 它允许您封装锁,因此您不会冒一些其他类干扰您的锁并可能使您的代码加密的风险。

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

答案 2 :(得分:1)

来自documentation

  

使这些方法同步有两个影响:

     

首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在执行时   对象的同步方法,所有其他调用的线程   同一对象块的同步方法(暂停执行)   直到第一个线程完成对象。

     

其次,当同步方法退出时,它会自动建立与之后的任何关系   调用同一对象的同步方法。这个   保证所有人都可以看到对象状态的变化   线程。

答案 3 :(得分:1)

仅在以下情况下显示输出:

 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) - 为true。和输出是可见的。

答案 4 :(得分:0)

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