java - 多线程环境下的单例模式

标签 java multithreading design-patterns singleton synchronized

在我的面试中,面试官以单例模式开始他的问题。我写在下面。然后,他问我们不应该在 getInstance 方法中检查 Nullity 吗?

我回答说,不需要,因为成员是静态类型并且同时被初始化。但是,他似乎对我的回答不满意。我是否正确?

class Single {

        private final static Single sing = new Single();       
        private Single() {
        }        
        public static Single getInstance() {
            return sing;
        }
    }

现在,下一个问题是为多线程环境编写单例类。然后,我编写了双重检查单例类。

  class MultithreadedSingle {        
        private static MultithreadedSingle single;       
        private MultithreadedSingle() {
        }        
        public static MultithreadedSingle getInstance() {
            if(single==null){
                    synchronized(MultithreadedSingle.class){
                      if(single==null){
                            single= new MultithreadedSingle(); 
                              }      
                      }
                   }
             return single;
        }
    }

然后,他反对使用synchronized 并仔细检查并说它没有用。为什么要检查两次,为什么要使用 synchronized ? 我试图用多种方案来说服他。但是,他没有。

后来,在家里我尝试了下面的代码,其中我使用了具有多线程的简单单例类。

public class Test {

    public static void main(String ar[]) {
        Test1 t = new Test1();
        Test1 t2 = new Test1();
        Test1 t3 = new Test1();
        Thread tt = new Thread(t);
        Thread tt2 = new Thread(t2);
        Thread tt3 = new Thread(t3);
        Thread tt4 = new Thread(t);
        Thread tt5 = new Thread(t);
        tt.start();
        tt2.start();
        tt3.start();
        tt4.start();
        tt5.start();

    }
}

final class Test1 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + Single.getInstance().hashCode());
        }
    }

}
     class Single {

        private final static Single sing = new Single();       
        private Single() {
        }        
        public static Single getInstance() {
            return sing;
        }
    }

下面是输出:

Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-0 : 1153093538
Thread-4 : 1153093538
Thread-1 : 1153093538
Thread-2 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-3 : 1153093538
Thread-2 : 1153093538
Thread-2 : 1153093538
Thread-2 : 1153093538
Thread-2 : 1153093538
Thread-1 : 1153093538
Thread-1 : 1153093538
Thread-1 : 1153093538
Thread-1 : 1153093538
Thread-4 : 1153093538
Thread-4 : 1153093538
Thread-4 : 1153093538
Thread-4 : 1153093538

所以,问题是,在多线程环境下是否有必要使用synchronize or/and double check 方法?似乎我的第一个代码本身(没有添加任何额外的代码行)就是这两个问题的答案。任何更正和知识共享将不胜感激。

最佳答案

你的第一个例子是绝对正确的,通常是单例的首选“惯用语”。另一种是制作单元素枚举:

public enum Single {
    INSTANCE;

    ...
}

除非类是可序列化的,否则这两种方法非常相似,在这种情况下,枚举方法更容易正确——但如果类不是可序列化的,我实际上更喜欢你的枚举方法,作为一种风格事情。当心由于实现接口(interface)或扩展本身可序列化的类而“意外地”变为可序列化。

关于双重检查锁示例中的第二次无效检查,您也是正确的。但是,sing 字段必须 volatile 才能在 Java 中工作;否则,在一个线程写入 sing 和另一个线程读取它之间没有正式的“先于发生”边缘。这可能导致第二个线程看到 null,即使第一个线程分配给变量,或者,如果 sing 实例有状态,它甚至可能导致第二个线程只看到部分状态(看到部分构造的对象)。

关于java - 多线程环境下的单例模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32152745/

相关文章:

java - 重构遗留代码——接口(interface)实现java——使用非接口(interface)方法

asp.net-mvc-3 - 使用 for 循环是在 ASP.NET MVC 应用程序中添加多个相似数据库条目的最优雅的方法吗? ( C# )

java - Poi-ooxml-schema 源或仅用于文档的 jar

java - 从字符串中的给定单词中获取下两个单词,同时包含非字母数字字符的单词

C++:OpenMP:为多线程复制函数指针

java - 令人困惑的多线程示例

php - 警告: preg_match() [function.preg-match]: No ending delimiter '/' found

Java线程错误

java - 在不迭代的情况下创建与另一个大小相同的多数组的快速方法?

C# 从 mysql 数据库获取代码关键部分的锁