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