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

java编程和问题java单线程多线程(单线程与多线程),java,multithreading,singleton,Java,Multithreading,Singleton,我有一个使用单例模式的程序。我需要使用线程,并记住在使用线程机械化之前和之后,输出应该是相同的。我的意思是避免出现“断模式”的情况,即线程忽略单个对象,而创建更多的对象。但是,我失败了。我尝试使用“同步”,但没有任何改变。同样的错误结果 我的主菜是Runnable public class Main implements Runnable { Main(){} public void run () { Counter[] cou

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

我的主菜是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(); 
    }
}

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

不要使用双重检查的单例模式,不管它是否损坏,这都不是现代的方式

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

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

}

您必须同步整个方法


在静态块中初始化singleton而不检查任何内容是最有意义的,除非您有数千个典型的未使用的singleton类,否则它不会影响性能。

不完全确定您想要实现什么,但有一些事情是错误的

  • 递增/递减
    不是线程安全的而不是原子操作。您要么需要同步这两种方法,要么使用原子递增/递减方法

  • 您正在读取与修改计数器不同的计数器值,因此另一个线程可能在更新和读取
    println
    之间更改为值。如果需要显示,我建议使用AtomicInteger并从递增/递减返回新值

  • 综上所述,您可能可以用静态AtomicInteger实例替换计数器

  • getCounter
    中的双重检查锁定可能已中断。我不确定在同步wrt到可见性之外,静态的行为是什么。为了安全起见,我会删除初始的空检查

  • 您所需的输出不会来自此代码。每个线程(其中有3个线程)使用相同的计数器实例,5次,每次打印两次。按我的计数,这是30条输出语句

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

  • 在类上进行同步是一种不好的做法,尤其是在公共类上,因为其他代码可能会在该类上进行同步并干扰您的锁定。最好的方法是使用
    私有静态最终对象锁=新对象()并在上面同步

  • 您不需要在return语句周围放置()命令


希望这能有所帮助!多线程代码是一项困难/危险的业务,因此请小心处理!如果你问了一个问题,说明你的目标,也许有人可以帮你提供一些提示和/或模型答案。

你错过了

private static volatile Counter myInstance;

这应该可以解决问题,但不要这样做。如图所示,使用Holder模式或eager初始化。或者更好的方法是,使用而不是。

在声明
实例时,您可能需要初始化
变量。值得注意的是类是延迟加载的,因此,仅使用一个
公共静态final
值或一个
enum
加一个值可能同样好,而且更简单/更安全。我认为在新的内存模型中,它可以工作,但只适用于易失性变量。如果整个方法是同步的,应该不会有问题,但它仍然很难看。我投票支持肖恩的答案。“有效的Java”是正确的方法。@duffymo:他指的是成千上万个不同的类,它们都是单例的,这确实是可能的。我知道他的意思。可能的对向往的达菲莫:我很高兴我们在这一点上意见一致。我试图指出,除非情况极端,否则静态初始化不是问题。“成千上万的单身汉”是一个夸张的说法。
java.util
应该有一个
Lazy
类来进行懒惰的评估;这是经常需要的,人们不知道如何正确地做。“内部类单例”和“枚举单例”是黑客攻击,只适用于全局状态。Spring只是让您用xml编写同样的东西。出于某种原因,人们认为xml不是代码。“这对他们的老板很好。”本施密特谢谢。维基百科文章不幸被更改,删除了链接
private static volatile Counter myInstance;