Java-программирование и проблема Java-многопоточности (синглтон против многопоточности) - PullRequest
0 голосов
/ 20 марта 2011

У меня есть программа, которая использует шаблон синглтона. Мне нужно использовать потоки, учитывая, что выходные данные должны быть одинаковыми до и после механизации потоков. Я имею в виду, чтобы избежать случая «сломанного шаблона», когда поток игнорирует синглтон и создает больше одного объекта. Но я потерпел неудачу. Я пытался использовать «синхронизированный», но ничего не изменилось. тот же неправильный результат.

Мой главный с Runnable

    public class Main implements Runnable  {
    Main(){}
    public void run ()
        { 
            Counter[] counters = new Counter[5];
            for(int i = 0; i < counters.length; i++) 
                {
                    counters[i] = Counter.getCounter();
                }
            for(int i = 0; i < counters.length; i++) 
                {
                    counters[i].increment();
                    System.out.println("counter[" + i + "] = " + counters[i]);
                }

            for(int i = 0; i < 5; i++) {

                counters[i].decrement();
                System.out.println("counter[" + i + "] = " + counters[i]);
                }}

    public static void main(String[] args)  
    {

        Main m1=new Main();
        Main m2=new Main();
        Main m3=new Main();
        new Thread(m1).start();
        new Thread(m2).start();
        new Thread(m3).start(); 
    }
}

Другой класс, который применяет шаблон синглтона

 public  class Counter {

        private static Counter myInstance = null;


        public static  Counter getCounter() 
        {
            if(myInstance == null) 
                {
                    synchronized (Counter.class)                    {
                        if(myInstance == null) 
                        {
                            myInstance = new Counter();
                        }
                    }   
                }




        return(myInstance);


        }

        private int myCounter;

        private Counter() {
        myCounter = 0;
        }

        public void increment() {
        myCounter++;
        }

        public void decrement() {
        myCounter--;
        }

        public String toString() {
        return(Integer.toString(myCounter));
        }
    }

Ответы [ 5 ]

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

Не используйте шаблон двойной проверки синглтона, это не современный способ сделать это, сломанный или нет.

Синглеты в Java должны быть реализованы с использованием внутреннего класса или перечисления (см. Эффективная Java Элемент 3: Применение свойства singleton с помощью частного конструктора или типа перечисления ):

а) Синглтон-модель внутреннего класса:

public class MySingleton{
    private MySingleton(){}
    private static class InstanceHolder{
        private static final MySingleton INSTANCE = new MySingleton(); 
    }
    public static MySingleton getInstance(){
        return InstanceHolder.INSTANCE;
    }

}

b) Enum Singleton Pattern

public enum MySingleton{
    INSTANCE;

    public static MySingleton getInstance(){
        return INSTANCE;
    }

}
4 голосов
/ 20 марта 2011

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

Помимо вопроса «Действительно ли мне нужен Синглтон?», Что за ленивое воплощение того, что Синглтон покупает вас?

Ничего .

Это может быть оправдано, если ваш синглтон был очень дорогим для создания экземпляра и есть вероятность, что вы не сможете его использовать.Но здесь нет ни одного случая.

Итак, если нужно, напишите это так:

public class Counter 
{
    // Edited at the recommendation of Sean and "Effective Java"; see below
    private static class InstanceHolder 
    {
        private static final Counter INSTANCE = new Counter(); 
    }

    private Counter() {}

    public static Counter getInstance()  { return InstanceHolder.INSTANCE; }

    // The rest of the implementation follows.
}
1 голос
/ 20 марта 2011

Не совсем уверен, что вы пытаетесь достичь, но есть несколько неправильных вещей.

  • increment/decrement не являются потокобезопасными.++ и - НЕ являются атомарными операциями.Вам нужно либо синхронизировать оба этих метода, либо использовать AtomicInteger с его атомарными методами увеличения / уменьшения

  • Вы читаете значение счетчика отдельно от его модификации, чтобы другой поток могизменилось на значение между обновлением и чтением для println.Я бы предложил использовать AtomicInteger и возвращать новое значение из приращения / убывания, если вам нужно отобразить его

  • Глядя на вышеупомянутые две точки, вы, вероятно, можете заменить Counter на статический экземпляр AtomicInteger

  • Возможно, блокировка с двойной проверкой в ​​getCounter сломана.Я не уверен, что поведение статики находится вне синхронизации с видимостью.Чтобы быть в безопасности, я бы убрал начальную нулевую проверку

  • Ваш требуемый вывод не будет выходить из этого кода.Каждая нить, из которых 3, берет один и тот же экземпляр Counter, 5 раз и печатает дважды.По моим подсчетам, это 30 выходных операторов.

  • Порядок этих выходных данных никогда не может быть предсказан, поскольку у вас есть потоки, конкурирующие за увеличение / уменьшение значения (одного) счетчика, так что оно может увеличитьсяи вниз случайным образом.Кроме того, печать числового значения может появляться не по порядку, так как потоки конкурируют за это тоже

  • Это плохая практика для синхронизации в классе, особенно в публичном классе, так как другой код может синхронизироватьна это и мешать твоей блокировке.Лучше всего иметь private static final Object lock = new Object(); и синхронизировать с ним

  • Вам не нужно ставить () вокруг операторов возврата

Надеждакое-что помогает!Многопоточный код - сложный / опасный бизнес, поэтому, пожалуйста, будьте осторожны!Возможно, если вы зададите вопрос с указанием вашей цели, кто-то может помочь вам с некоторыми советами и / или типовым ответом.

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

Вы пропустили volatile в

private static volatile Counter myInstance;

Это должно исправить это, но не делать этого. Используйте шаблон держателя или активную инициализацию, как показано. Или лучше использовать Dependency Injection вместо Singleton antipattern .

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

Вы должны синхронизировать весь метод. Вот почему ваш код неправильный

Наиболее целесообразно инициализировать синглтон в статическом блоке и ничего не проверять, если только у вас нет тысяч обычно неиспользуемых одноэлементных классов, это не повлияет на производительность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...