>  기사  >  Java  >  JAVA 디자인 패턴 싱글턴 패턴

JAVA 디자인 패턴 싱글턴 패턴

高洛峰
高洛峰원래의
2016-12-12 13:47:241185검색

이 기사에서는 23가지 디자인 패턴 시리즈 중 싱글턴 패턴을 계속해서 소개합니다.

개념:
싱글톤 패턴은 Java의 일반적인 디자인 패턴입니다. 싱글톤 패턴을 작성하는 방법에는 여러 가지가 있습니다. 게으른 싱글톤, 등록된 싱글톤입니다. 하나씩 일어나는 것.
 싱글턴 모드의 특징은 다음과 같습니다.
 1. 싱글턴 클래스는 인스턴스를 하나만 가질 수 있습니다.
2. 싱글톤 클래스는 고유한 인스턴스를 생성해야 합니다.
 3. 싱글톤 클래스는 이 인스턴스를 다른 모든 객체에 제공해야 합니다.
싱글톤 패턴은 클래스에 인스턴스가 하나만 있도록 보장하고 자체적으로 인스턴스화하며 이 인스턴스를 전체 시스템에 제공합니다. 컴퓨터 시스템에서 스레드 풀, 캐시, 로그 개체, 대화 상자, 프린터 및 그래픽 카드 드라이버 개체는 종종 싱글톤으로 설계됩니다. 이러한 응용 프로그램은 모두 리소스 관리자의 기능을 어느 정도 갖추고 있습니다. 각 컴퓨터에는 여러 대의 프린터가 있을 수 있지만 두 개의 인쇄 작업이 동시에 프린터로 출력되는 것을 방지하기 위해 프린터 스풀러는 하나만 있을 수 있습니다. 각 컴퓨터에는 여러 개의 통신 포트가 있을 수 있으며, 하나의 통신 포트가 동시에 두 개의 요청에 의해 호출되는 것을 방지하기 위해 시스템은 이러한 통신 포트를 중앙에서 관리해야 합니다. 간단히 말해서, 싱글톤 모드를 선택하는 목적은 일관성 없는 상태를 피하고 장기적인 정책을 피하는 것입니다.


1. Lazy Singleton

//懒汉式单例类.在第一次调用的时候实例化自己   
public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    //静态工厂方法   
    public static Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
    }  
}

Singleton은 생성 방법을 private으로 제한하여 클래스가 외부에서 인스턴스화되는 것을 방지합니다. 인스턴스는 getInstance() 메서드를 통해서만 액세스할 수 있습니다.

(실제로 Java 리플렉션 메커니즘을 통해 개인 생성자를 사용하여 클래스를 인스턴스화할 수 있으며, 이는 기본적으로 모든 Java 싱글톤 구현을 무효화합니다. 이 문제는 여기서 논의되지 않으므로 숨기겠습니다. 현재로서는 반사 메커니즘이 존재하지 않는 것으로 간주됩니다. )

그러나 위의 게으른 싱글톤 구현은 스레드 안전 문제를 고려하지 않습니다. 스레드 안전성을 달성하기 위해 다음 세 가지 메소드가 있으며, 모두 getInstance 메소드를 수정하여 처음으로 싱글톤 모드에 노출되었지만 그렇지 않은 경우 스레드 안전성을 보장합니다. 스레드 안전에 대해 많이 알고 있으면 먼저 다음 세 단계를 건너뛰고 Hungry 스타일 싱글톤을 살펴본 다음 읽은 후 돌아가서 스레드 안전 문제를 고려해 보세요.


1. getInstance 메소드에 동기화 추가

public static synchronized Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
}

2. 이중 확인 잠금

public static Singleton getInstance() {  
        if (singleton == null) {    
            synchronized (Singleton.class) {    
               if (singleton == null) {    
                  singleton = new Singleton();   
               }    
            }    
        }    
        return singleton;   
    }

3. 이는 위의 1과 2보다 낫습니다. 스레드 안전성을 달성할 뿐만 아니라 동기화로 인한 성능 영향을 피합니다.

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




2. 배고픈 중국식 싱글

때에 배고픈 중국식 클래스가 생성되고 시스템 사용을 위해 정적 개체가 생성되며 나중에 변경되지 않으므로 본질적으로 스레드로부터 안전합니다.

//饿汉式单例类.在类初始化时,已经自行实例化   
public class Singleton1 {  
    private Singleton1() {}  
    private static final Singleton1 single = new Singleton1();  
    //静态工厂方法   
    public static Singleton1 getInstance() {  
        return single;  
    }  
}


3. 등록된 싱글턴(무시가능)

등록된 싱글턴은 실제로 유지된다. 등록된 인스턴스는 Map에서 직접 반환되며, 등록되지 않은 인스턴스는 먼저 등록된 후 반환됩니다.

//类似Spring里面的方法,将类名注册,下次从里面直接获取。  
public class Singleton3 {  
    private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();  
    static{  
        Singleton3 single = new Singleton3();  
        map.put(single.getClass().getName(), single);  
    }  
    //保护的默认构造子  
    protected Singleton3(){}  
    //静态工厂方法,返还此类惟一的实例  
    public static Singleton3 getInstance(String name) {  
        if(name == null) {  
            name = Singleton3.class.getName();  
            System.out.println("name == null"+"--->name="+name);  
        }  
        if(map.get(name) == null) {  
            try {  
                map.put(name, (Singleton3) Class.forName(name).newInstance());  
            } catch (InstantiationException e) {  
                e.printStackTrace();  
            } catch (IllegalAccessException e) {  
                e.printStackTrace();  
            } catch (ClassNotFoundException e) {  
                e.printStackTrace();  
            }  
        }  
        return map.get(name);  
    }  
    //一个示意性的商业方法  
    public String about() {      
        return "Hello, I am RegSingleton.";      
    }      
    public static void main(String[] args) {  
        Singleton3 single3 = Singleton3.getInstance(null);  
        System.out.println(single3.about());  
    }  
}
여기서는 등록 스타일 싱글턴을 무시할 수 있다고 표시했습니다. 게다가 정적 메서드 블록 때문에 내부 구현에서는 여전히 중국 스타일 싱글턴을 사용합니다. 그것의 싱글톤은 클래스가 로드될 때 인스턴스화됩니다.

배고픈 남자와 게으른 남자의 차이

이름부터 배고픈 남자와 게으른 남자,

배고픈 남자가 급이다 일단 로드되면 싱글톤 초기화가 완료되어 getInstance가 호출될 때 싱글톤이 이미 존재하는지 확인하고

게으른 사람들은 게으르고 getInstance가 호출될 때만 싱글톤을 초기화하기 위해 돌아갑니다.

또한 다음 두 가지 방법은 다음 두 가지 점에서 구별됩니다.

1. 실 안전:

배고픈 중국식 탄생 스레드로부터 안전하며 문제 없이 멀티스레딩에 직접 사용할 수 있습니다.

게으른 스타일 자체는 스레드로부터 안전하지 않습니다. 즉, 1, 2, 위의 3개는 리소스 로딩과 성능에 약간의 차이가 있습니다.


2. 리소스 로딩 및 성능:

Hungry Chinese 스타일은 나중에 무슨 일이 일어나든 관계없이 클래스가 생성될 때 정적 개체를 인스턴스화합니다. 이 싱글턴을 사용하지 않으면 일정량의 메모리를 차지하게 되지만, 그에 따라 리소스가 초기화되어

되었기 때문에 처음 호출할 때 속도가 더 빨라집니다. 게으른 스타일은 이름에서 알 수 있듯이 로딩을 지연시키며, 싱글톤이 처음 사용될 때 객체가 인스턴스화됩니다. 수행할 작업이 많으면 초기화해야 합니다. , 성능이 약간 지연되고 배고픈 중국 스타일과 동일하게됩니다.

세 가지 구현 1, 2, 3에는 몇 가지 차이점이 있습니다.

첫 번째는 메서드 호출에 동기화를 추가하지만 매회 동기화해야 합니다. 결국, 99%의 경우에는 동기화가 필요하지 않습니다.

두 번째 방법은 getInstance에서 두 번의 null 검사를 수행하여 싱글톤이 호출될 때만 수행되는지 확인하는 것입니다. 첫 번째 동기화는 스레드로부터 안전하며 매번 동기화의 성능 손실을 방지합니다

세 번째 방법은 클래스 로더 메커니즘을 사용하여 인스턴스를 초기화할 때 스레드가 하나만 있도록 보장합니다. 또한 스레드로부터 안전하고 성능 손실이 없으므로 일반적으로 이것을 사용하는 경향이 있습니다.

什么是线程安全?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。


应用

以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:

public class TestSingleton {  
    String name = null;  
  
        private TestSingleton() {  
    }  
  
    private static volatile TestSingleton instance = null;  
  
    public static TestSingleton getInstance() {  
           if (instance == null) {    
             synchronized (TestSingleton.class) {    
                if (instance == null) {    
                   instance = new TestSingleton();   
                }    
             }    
           }   
           return instance;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public void printInfo() {  
        System.out.println("the name is " + name);  
    }  
  
}

可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

还有疑问可参考http://www.iteye.com/topic/652440
和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

public class TMain {  
    public static void main(String[] args){  
        TestStream ts1 = TestSingleton.getInstance();  
        ts1.setName("jason");  
        TestStream ts2 = TestSingleton.getInstance();  
        ts2.setName("0539");  
          
        ts1.printInfo();  
        ts2.printInfo();  
          
        if(ts1 == ts2){  
            System.out.println("创建的是同一个实例");  
        }else{  
            System.out.println("创建的不是同一个实例");  
        }  
    }  
}

 运行结果:

JAVA 디자인 패턴 싱글턴 패턴

结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.