从Singleton类返回的Object引用是Thread安全吗?

时间:2014-12-27 09:36:56

标签: java thread-safety singleton

我想从单例类返回一个对象,该对象由多个线程使用,并且还在单例类中使用两个方法。返回该对象引用是否安全?请参阅我的样本

public class MainClass {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run() {
                MyObject rv = Singleton.getInstance().get();

                for (int i = 0; i < 100; i++) {
                    System.out.println("THREAD 0 : " + rv.getCount());
                }
            }
        };
        t.start();

        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    Singleton.getInstance().update();
                }
            }
        };
        t1.start();
    }
}    

import java.util.HashMap;

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        MyObject rv = new MyObject(1, 1);
        hashmap.put(1, rv);
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    private final HashMap<Integer, MyObject> hashmap = new HashMap<>();

    public MyObject get() {

        return hashmap.get(1);
    }

    public void update() {
        hashmap.get(1).setCount(hashmap.get(1).getCount() + 1);
    }
}     

public class MyObject {
    int count;
    int id;

    MyObject(int id, int count) {
        this.id = id;
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

2 个答案:

答案 0 :(得分:0)

根据MyObject的使用方式,您还需要保护对字段的访问权。

public enum Singleton { 
    INSTANCE;

    public static Singleton getInstance() {
        return INSTANCE;
    }

    private final Map<Integer, MyObject> map = new HashMap<>();

    public synchronized int getCount(int key) {
        MyObject mo = map.get(key);
        if (mo == null)
            return 1;
        return mo.getCount();
    }

    public synchronized void update(int key) {
        MyObject mo = map.get(key);
        if (mo == null)
            map.put(key, mo = new MyObject(1, 1));
        mo.incrementCount();
    }
}

在MyObject类中,您将添加一个方法

public void incrementCount() {
    count++;
}

注意:由于您无法直接访问基础MyObject,因此无需添加其他同步。


对象的引用是线程安全的,但synchronized块之外的访问都不是线程安全的。

注意:线程之间没有协调,一个可以在另一个开始之前完成。增加一个计数器比打印到控制台要快得多,所以你应该看到计数器跳了很多,实际上它可能会从0跳到100,相隔一行。 事实上,你把它放在了错误的地方,你可以这样做。

public enum Singleton { 
    INSTANCE;

private Singleton() {
    hashmap.put(1, new MyObject(1, 1));
}

public static Singleton getInstance() {
    return INSTANCE;
}

private final Map<Integer, MyObject> hashmap = new HashMap<>();

public synchronized MyObject get() {
    // safe is all you need to do is read one value.
    return hashmap.get(1);
}

public synchronized void update() {
    hashmap.get(1).setCount(hashmap.get(1).getCount() + 1);
}
}

甚至更简单

public enum Singleton { 
    INSTANCE;
    private final MyObject myObj = new MyObject(1, 1);

    public static Singleton getInstance() {
        return INSTANCE;
    }

    public synchronized MyObject get() {
        // safe is all you need to do is read one value.
        return myObj;
    }

    public synchronized void update() {
        myObject.setCount(myObject.getCount() + 1);
    }
}

答案 1 :(得分:-1)

您必须使Object线程安全,因为此类的实例由不同的线程使用(请注意同步的方法:

public class MyObject {
    int count;
    int id;

    MyObject(int id, int count) {
        this.id = id;
        this.count = count;
    }

    public synchronized int getCount() {
        return count;
    }

    public synchronized void setCount(int count) {
        this.count = count;
    }

    public synchronized int getId() {
        return id;
    }

    public synchronized void setId(int id) {
        this.id = id;
   }
}