Java 从单例类返回的对象引用是线程安全的吗?

Java 从单例类返回的对象引用是线程安全的吗?,java,thread-safety,singleton,Java,Thread Safety,Singleton,我想从一个singleton类返回一个对象,这个对象被许多线程使用,在singleton类中还有两个方法。返回该对象引用是否安全?请看我的样品 public class MainClass { public static void main(String[] args) { Thread t = new Thread() { public void run() { MyObject rv = Singleton.ge

我想从一个singleton类返回一个对象,这个对象被许多线程使用,在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();
    }
}    

根据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的直接访问权,因此不需要添加额外的同步


对对象的引用是线程安全的,但是
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);
}
}

根据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的直接访问权,因此不需要添加额外的同步


对对象的引用是线程安全的,但是
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);
}
}

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

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;
   }
}

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

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;
   }
}

呃,不,你为什么会这样认为?一个对象是线程安全的,当且仅当它是线程安全的。单例与它无关。你让getInstance()成为已同步,但在获得singleton实例后执行的任何操作都不是线程安全的。从正面来看,我认为不需要。但我可能错了。您有任何理由在此处使用hashmap吗?如果没有,请放弃它。您可能想进行更新()方法也是同步的,如果您同时从不同的线程使用它,否则一些更新请求可能会丢失。错误,否,您为什么会这样认为?对象是线程安全的,当且仅当它是线程安全的。Singleton与它无关。您使getInstance()已同步,但在获得singleton实例后执行的任何操作都不是线程安全的。从正面来看,我认为不需要。但我可能错了。您有任何理由在此处使用hashmap吗?如果没有,请放弃它。您可能想进行更新()方法也是同步的,如果您同时从不同的线程使用它,否则一些更新请求可能会丢失。当
Singleton.getInstance().get()时,这个线程安全性如何
返回MyObject实例,该实例可能很高兴被任何喜欢这样做的线程修改为不同步?我不知道为什么我的答案被否决,但您需要对MyObject进行同步。@P.J.Meisch隐含的用例是从MyObject中读取一个值,为此,您不需要另一个同步值和get()中的一个值提供了一个内存障碍。问题是:该对象被多个线程以及singleton类中的两个方法使用。在本示例中,返回对象的唯一用途可能是使用它的
getCount()
。但是如果这是一个示例,说明了一个更大的问题,那么您可能会遇到麻烦。当
Singleton.getInstance().get()时,这个线程安全性如何
返回MyObject实例,该实例可能很高兴被任何喜欢这样做的线程修改为不同步?我不知道为什么我的答案被否决,但您需要对MyObject进行同步。@P.J.Meisch隐含的用例是从MyObject中读取一个值,为此,您不需要另一个同步值和get()中的一个值提供了一个内存障碍。问题是:该对象被多个线程以及singleton类中的两个方法使用。在本示例中,返回对象的唯一用途可能是使用它的
getCount()
。但如果这是一个示例,说明了一个更大的问题,那么您可能会遇到麻烦。我没有否决投票,但这不会帮助任何其他代码线程安全,例如
hashmap.get(1).setCount(hashmap.get(1).getCount()+1)即使您进行了更改,
也会在许多方面被破坏。我没有否决投票,但这无助于任何其他代码线程安全,例如,
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 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;
   }
}