Java: синглтон, статическая переменная и безопасность потока - PullRequest
2 голосов
/ 17 марта 2011
class MyClass
{
private static MyClass obj;

public static MyClass getInstance()
{
    if(obj==null)
    {
        obj = new MyClass();
    }
    return obj;
}

В приведенном выше примере кода Java, поскольку obj является статической переменной внутри класса, getInstance все еще будет не безопасным для потоков? Поскольку статические переменные являются общими для всех потоков, 2 одновременных потока должны использовать один и тот же объект. Не так ли?

Випул Шах

Ответы [ 6 ]

6 голосов
/ 17 марта 2011

Поскольку статические переменные широко используются, они чрезвычайно безопасны для потоков.

Подумайте, что произойдет, если два потока вызывают ваш getInstance одновременно.Оба потока будут смотреть на общую статическую obj, и оба потока увидят, что obj пуст в проверке if.Затем оба потока создадут новое obj.

. Вы можете подумать: «эй, это потокобезопасно, поскольку у obj будет только одно значение, даже если оно инициализируется несколько раз».Есть несколько проблем с этим утверждением.В нашем предыдущем примере вызывающие getInstance оба получат свои obj обратно.Если оба абонента сохранят свои ссылки на obj, то у вас будет несколько экземпляров используемого синглтона.

Даже если вызывающие абоненты в нашем предыдущем примере просто сделали: MyClass.getInstance(); и не сохранили ссылку начто вернуло MyClass.getInstance();, вы все равно можете получить разные экземпляры из getInstance в этих потоках.Вы даже можете попасть в состояние, при котором новые экземпляры obj создаются, даже когда вызовы getInstance не происходят одновременно!

Я знаю, что мое последнее утверждение кажется нелогичным, так как последнее назначение objКазалось бы, единственное значение, которое может быть возвращено из будущих вызовов на MyClass.getInstance().Однако вы должны помнить, что каждый поток в JVM имеет свой собственный локальный кэш основной памяти.Если два потока вызывают getInstance, их локальные кэши могут иметь разные значения, назначенные obj, и будущие вызовы getInstance из этих потоков будут возвращать то, что находится в их кэшах.

Самый простой способ убедиться, что поток getInstance безопасенбыло бы сделать метод синхронизированным.Это гарантирует, что

  1. Два потока не могут одновременно войти в getInstance
  2. Потоки, пытающиеся использовать obj, никогда не получат устаревшее значение obj из своего кэша

Не Не пытайтесь стать умным и используйте двойную проверку блокировки: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

2 голосов
/ 17 марта 2011

в этом случае getInstance () не является поточно-ориентированным, даже если вы используете статическую переменную.только синхронизация делает этот потокобезопасным.

2 голосов
/ 17 марта 2011

Хорошее объяснение можно найти здесь:

http://en.wikipedia.org/wiki/Singleton_pattern

Статья в вики освещает различные многопоточные подходы, а также некоторые их плюсы и минусы.

0 голосов
/ 25 марта 2013
class MyClass
{
private static MyClass obj;

private MyClass(){
    // your initialization code
}
public static synchronized MyClass getInstance()
{
    if(obj==null)
    {
        obj = new MyClass();
    }
    return obj;
}

Я согласен с @Manoj.Я полагаю, что вышеизложенное будет одним из лучших способов достижения одноэлементной цели.И синхронизация делает поток объекта безопасным.Даже статично :) 1002 *

0 голосов
/ 17 марта 2011
public class Singleton{
    private static transient volatile Singleton instance;
    public static Singleton getInstance(){
        if(instance==null)synchronized(Singleton.class){
            if(instance==null){
                instance = new Singleton();
            }
        }
        return instance;
    }
    private Singleton(){
        /*....*/
    }
}

Страница 182: http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&dq=design+patterns&hl=de&ei=EFGCTbyaIozKswbHyaiCAw&sa=X&oi=book_result&ct=result&resnum=2&ved=0CDMQ6AEwAQ#v=onepage&q&f=false

Думаю, это можно пометить как ответивший сейчас

0 голосов
/ 17 марта 2011

В следующем примере показан странный поток сохранения модифицированного шаблона в одну тонну, который также поддерживает обобщенные элементы.

Чтобы сохранить только поток и сохранить синхронизацию, просто возьмите синхронизированный блок и временные и переменные ключевые слова. Обратите внимание, что существует двойная проверка, синхронизированный блок находится внутри if. Это повышает производительность, поскольку синхронизация обходится дорого.

Конечно, для настоящего синглтона не используйте карты, я сказал, что это модифицированный.

public class Edge<T> {
    @SuppressWarnings({"unchecked"})
    private static transient volatile HashMap<Object,HashMap<Object, Edge>> instances = new HashMap<Object, HashMap<Object,Edge>>();
    /**
     * This function is used to get an Edge instance
     * @param <T> Datatype of the nodes.
     * @param node1, the source node
     * @param node2, the destination node
     * @return the edge of the two nodes.
     */
    @SuppressWarnings({"unchecked"})
    public static <T> Edge<T> getInstance(T node1, T node2){
        if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){
            synchronized (Edge.class) {
                if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){
                    Edge<T> edge = new Edge<T>(node1, node2);
                    if(!instances.containsKey(node1)){
                        instances.put(node1, new HashMap<Object, Edge>());
                    }
                    instances.get(node1).put(node2, edge);
                }
            }
        }
        return (Edge<T>)instances.get(node1).get(node2);
    }
...