java编程和问题java单例多线程(单例与多线程)

时间:2011-03-19 21:22:52

标签: java multithreading singleton

我有一个使用单例模式的程序。我需要使用它的线程,记住在使用线程机械化之前和之后输出应该是相同的。我的意思是避免“破碎模式”的情况,其中线程忽略单例并创建更多的一个对象。但是,我失败了。我试图使用“同步”,但没有任何改变。同样的错误结果。

我主要使用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 个答案:

答案 0 :(得分:6)

不要使用双重检查的单一模式,这不是现代的方式,无论它是否被破坏。

Java中的单例应该使用内部类或枚举实现(请参阅Effective Java第3项:使用私有构造函数或枚举类型强制执行单例属性):

a)内部类单身人士模式:

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

}

b)枚举单身人士模式

public enum MySingleton{
    INSTANCE;

    public static MySingleton getInstance(){
        return INSTANCE;
    }

}

答案 1 :(得分:4)

双重检查锁定曾在Java中被破坏。我不知道新的内存模型是否修复了它。

除了“我真的需要一个单身人士吗?”这个问题之外,究竟是什么单身人士的懒惰实例购买你呢?

没有

如果您的Singleton实例化非常昂贵并且您可能不会使用它,那么这可能是合理的。但这里没有一个是这样的。

所以,如果你必须,那就这样写:

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

答案 2 :(得分:1)

不完全确定你想要实现的目标,但有一些问题。

  • increment/decrement不是线程安全的。 ++和 - 不是原子操作。您需要同步这两种方法或使用AtomicInteger及其原子递增/递减方法

  • 您正在读取计数器的值,而不是对其进行修改,因此在更新和读取println之间,另一个线程可能已更改为值。我建议使用AtomicInteger并在需要显示时从递增/递减中返回新值

  • 查看以上两点,您可以用静态AtomicInteger实例替换Counter

  • getCounter中的双重检查锁定可能已被破坏。我不确定静态的行为是否与可见性同步。为了安全起见,我会删除最初的空检查

  • 您的所需输出不会出现在此代码中。每个线程(其中有3个)采用相同的Counter实例,5次,每次打印两次。根据我的统计,这是30个输出语句。

  • 这些输出的顺序永远无法预测,因为您有线程竞争递增/递减(单个)计数器值,因此它可能以随机方式上下颠簸。此外,数字值的打印可能会出现故障,因为线程也在竞争

  • 在类(尤其是公共类)上进行同步是不好的做法,因为其他代码可以在其上进行同步并干扰您的锁定。最好的方法是拥有private static final Object lock = new Object();并同步

  • 您不需要在返回语句周围添加()

希望其中一些有帮助!多线程代码是一项困难/危险的业务,请谨慎行事!也许如果你问一个陈述你的目标的问题,有人可以帮你解决一些提示和/或模型答案。

答案 3 :(得分:0)

您必须同步整个方法。 Here's why your code is wrong

最有必要在静态块中初始化单例并且不检查任何内容,除非你有数千个通常未使用的单例类,否则它不会影响性能。

答案 4 :(得分:0)

你错过了volatile

private static volatile Counter myInstance;

这应该解决它,但不要这样做。如图所示,使用Holder Pattern或eager初始化。或者更好的是,使用Dependency Injection代替Singleton antipattern