디자인 패턴 학습 2: 싱글톤 패턴

싱글톤 패턴

클래스의 소위 싱글톤 디자인 패턴은 전체 소프트웨어 시스템에서 특정 클래스에 대해 하나의 개체 인스턴스만 존재할 수 있도록 특정 메서드를 사용하고 해당 클래스는 해당 클래스를 얻기 위해 하나의 메서드(정적 메서드)만 제공하도록 하는 것입니다. 개체 인스턴스.
데이터 저장 소스에 대한 프록시 역할을 하고 Session 객체 생성을 담당하는 Hibernate의 SessionFactory와 같은 것입니다. SessionFactory는 경량이 아니며 일반적으로 프로젝트에는 싱글톤 패턴인 SessionFactory 하나만 있으면 됩니다.

싱글톤 패턴을 사용하는 방법은 8가지가 있습니다.

  1. 배고픈 중국 스타일(정적 상수)
  2. 배고픈 중국 스타일(정적 코드 블록)
  3. 게으른(안전하지 않은 스레드)
  4. Lazy(스레드 안전, 동기화된 메서드)
  5. Lazy(스레드 안전, 동기화된 코드 블록)
  6. 이중 점검
  7. 정적 내부 클래스
  8. 세다

배고픈 중국어

1. 정적 상수
배고픈 중국어(정적 상수) 적용 예
단계는 다음과 같습니다.

  1. 생성자 사유화(클래스 내부의 생성자에 대한 외부 액세스를 방지하기 위해, 즉 new 키워드를 사용하여 객체를 생성할 수 없음)
  2. 클래스 내부에서 생성된 객체
  3. 정적 공개 메서드를 노출합니다. getInstance
  4. 암호

예시:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//饿汉式(使用静态变量实现)
class SingleTon{
    
    
    //1、构造器私有化,对外不提供new
    private SingleTon(){
    
    
    }

    //2、本类内部创建对象实例
    private final static SingleTon instance = new SingleTon();

    //3、提供一个公有的静态方法,返回实例对象
    public static SingleTon getInstance(){
    
    
        return instance;
    }
}

public class Type1 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);//结果是true,证明两个对象相等
    }
}

장단점 분석
장점: 이 작성 방법은 비교적 간단합니다. 즉, 클래스가 로드되면 인스턴스화가 완료됩니다. 스레드 동기화 문제를 방지합니다 .

단점: 클래스가 로드될 때 인스턴스화가 완료되어 Lazy Loading 효과를 얻지 못합니다 . 인스턴스가 처음부터 끝까지 사용되지 않으면 메모리가 낭비됩니다.

이 방법은 ClassLoder 메커니즘에 기반한 다중 쓰레드의 동기화 문제를 피하지만, 인스턴스는 클래스가 로드될 때 인스턴스화됩니다. 싱글톤 모드에서는 대부분이 getInstance 메소드를 호출합니다. 그러나 클래스를 로드하는 데에는 여러 가지 이유가 있으므로 클래스 로딩을 유발하는 다른 방법(또는 다른 정적 메서드)이 있는지 확인하는 것은 불가능합니다.이때 인스턴스를 초기화해도 지연 로딩 효과를 얻을 수 없습니다.

결론: 이 싱글톤 패턴은 사용 가능하며 메모리 낭비를 유발할 수 있습니다 .

2. 정적 코드 블록
예:

//饿汉式(使用静态代码块实现)
class SingleTon{
    
    

    private SingleTon(){
    
    
    }

    private static SingleTon instance;

    //在静态代码块中创建单例对象
    static {
    
    
        instance = new SingleTon();
    }
    
    public static SingleTon getInstance(){
    
    
        return instance;
    }
}

장단점 분석
이 방법은 클래스 인스턴스화 과정이 정적 코드 블록에 위치한다는 점을 제외하면 위의 방법과 유사하며, 클래스가 로드되면 정적 코드 블록의 코드를 실행하여 인스턴스를 초기화합니다. 수업. 장점과 단점은 위와 같습니다.

결론: 이 싱글톤 패턴을 사용할 수 있지만 메모리 낭비가 발생할 수 있습니다.

게으른

1. 스레드에 안전하지 않은 쓰기
예:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//懒汉式(线程不安全)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    //即懒汉式
    public static SingleTon getInstance(){
    
    
        if (instance==null){
    
    
            instance = new SingleTon();
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

장점과 단점 분석
장점: Lazy Loading 효과.

단점: 단일 쓰레드에서만 사용이 가능하다 멀티 쓰레드에서 if(singleton == null) 판단문 블록에 쓰레드가 진입하면 실행할 시간이 없고 다른 쓰레드도 이 판단문을 통과한다. 이 때 여러 인스턴스가 생성됩니다.

결론: 실제 개발에서는 이 방법을 사용하지 마십시오.

2. 스레드로부터 안전한 동기화 메서드
예:

//懒汉式(线程安全,同步方法)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //加入了同步方法,解决了线程不安全的问题
    public static synchronized SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            instance = new SingleTon();
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

장점 및 단점 분석
장점: 스레드 불안정 문제 해결

단점: 효율성이 너무 낮음 각 스레드가 클래스의 인스턴스를 얻으려는 경우 getInstance() 메서드의 실행을 동기화해야 합니다. 실제로 이 메소드는 인스턴스화 코드를 한 번만 실행하기에 충분하며, 나중에 이 클래스의 인스턴스를 얻으려면 직접 반환하면 됩니다. 이 방법은 동기화에 너무 비효율적입니다.

결론: 실제 개발에서는 이 방법을 권장하지 않습니다.

3. 안전하지 않은 동기화된 코드 블록 스레드
예:

//懒汉式(线程不安全,同步代码块)
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }
    
    public static SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            //同步代码块
            synchronized (SingleTon.class){
    
    
                instance = new SingleTon();
            }
        }
        return instance;
    }
}

public class Type2 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

장단점 분석
이 방법은 네 번째 구현 방법을 개선하기 위한 것으로 이전 동기화 방법의 효율성이 너무 낮고 인스턴스화된 코드 블록을 생성하도록 동기화하도록 변경되었습니다.
그러나 이 동기화는 스레드 동기화의 역할을 하지 않습니다! ! ! 세 번째 구현 방법에서 발생하는 상황과 일관되게 스레드가 if(싱글톤 == null) 판단 블록에 들어가고 아직 실행할 시간이 없는 경우 다른 스레드도 판단 문을 통과하면 여러 인스턴스 를 생성합니다.

결론: 실제 개발에서는 이 방법을 사용할 수 없습니다.

이중 점검

예시:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//双重检查(线程安全,线程同步)
class SingleTon{
    
    
	//volatile使instance变量的状态在各个线程之间是可见的
    private static volatile SingleTon instance;

    private SingleTon(){
    
    }

    //解决线程安全的问题,同时解决懒加载的问题,同时解决懒加载问题
    public static SingleTon getInstance(){
    
    
        if (instance == null){
    
    
            //同步代码块
            synchronized (SingleTon.class){
    
    
                if (instance == null){
    
    
                    instance = new SingleTon();
                }
            }
        }
        return instance;
    }
}
public class Type3 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

위의 코드 분석에 따르면 3개의 스레드 a, b, c가 getInstance() 메소드에 진입하면 스레드 a와 b는 함께 첫 번째 if(instance == null) 판단문을 입력하고 스레드 a가 스레드 b보다 앞선다. . 먼저 동기화 코드 블록을 입력합니다. 이때, 동기화 코드 블록에서는 하나의 스레드만 실행할 수 있으므로 a 스레드는 먼저 인스턴스를 초기화한 다음 동기화 코드 블록을 종료합니다. b 스레드가 동기화 코드 블록에 들어갈 수 있도록 동기화 코드 블록을 종료합니다. b 스레드가 동기화 코드 블록에 들어간 후 a 스레드가 이미 인스턴스를 초기화했기 때문에 b 스레드는 인스턴스를 다시 초기화하지 않고 직접 종료합니다. 동기화 코드 블록; 초기화되어 판단문에 입력되지 않았습니다.

장점 및 단점 분석
장점: 스레드 안전성, 지연 로딩, 고효율

Double-Check의 개념은 멀티 쓰레드 개발에서 많이 사용되는데, 위의 코드와 같이 쓰레드 안전성을 보장하기 위해 if(singleton == null) 검사를 두 번 수행했습니다.
이런 식으로 인스턴스화 코드는 한 번만 실행하면 되며 나중에 다시 액세스할 때 (싱글톤 == null) 여부를 판단하여 인스턴스화된 개체를 직접 반환하고 반복되는 메서드 동기화를 방지합니다.

결론: 실제 개발에서는 이 싱글톤 디자인 패턴을 권장합니다.

정적 내부 클래스

예시:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//静态内部类实现
class SingleTon{
    
    
    private static SingleTon instance;

    private SingleTon(){
    
    }

    //写一个静态内部类,该类中有一个静态属性
    private static class SingleTonInstance{
    
    
        private static final SingleTon INSTANCE = new SingleTon();
    }

    //提供一个公有方法,直接返回SingleTonInstance.INSTANCE
    public static SingleTon getInstance(){
    
    
        return SingleTonInstance.INSTANCE;
    }
}

public class Type4 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.getInstance();
        SingleTon instance2 = SingleTon.getInstance();
        System.out.println(instance == instance2);
    }
}

코드 분석, 우리는 SingleTon에 정적 ​​내부 클래스 SingleTonInstance를 작성했으며, SingleTon 클래스가 로드될 때 SingleTonInstance는 즉시 로드되지 않으며, SingleTonInstance 클래스는 getInstance() 메서드에서 SingleTonInstance 클래스의 정적 속성 INSTANCE가 호출될 때까지 로드되지 않습니다. 지연 로딩의 효과를 얻기 위해 로드되며 JVM 클래스 로딩 프로세스는 스레드로부터 안전합니다.

장점 및 단점 분석
이 방법은 인스턴스가 초기화될 때 스레드가 하나만 있도록 클래스 로딩 메커니즘을 채택합니다.
정적 내부 클래스 메서드는 Singleton 클래스가 로드될 때 즉시 인스턴스화되지 않지만 인스턴스화해야 할 때 getInstance 메서드가 호출되고 SingletonInstance 클래스가 로드되어 Singleton의 인스턴스화를 완료합니다.
클래스의 정적 속성은 클래스가 처음 로드될 때만 초기화되므로 여기서 JVM은 스레드 안전성을 보장하는 데 도움이 되며 클래스가 초기화되면 다른 스레드는 들어갈 수 없습니다.

장점: 스레드 불안정 방지, 정적 내부 클래스 기능을 사용하여 지연된 로딩, 고효율 달성

결론: 사용을 권장합니다.

세다

예시:

/**
 * @author 小关同学
 * @create 2021/9/13
 */
//枚举(使用枚举也可以实现单例)
enum SingleTon{
    
    
    INSTANCE;   //属性
    public void sayOK(){
    
    
        System.out.println("ok~");
    }
}

public class Type5 {
    
    
    public static void main(String[] args) {
    
    
        SingleTon instance = SingleTon.INSTANCE;
        SingleTon instance2 = SingleTon.INSTANCE;
        System.out.println(instance == instance2);
    }
}

장점과 단점 분석
이것은 JDK1.5에 추가된 열거의 도움으로 싱글톤 패턴을 구현합니다. 다중 스레드 동기화 문제를 방지할 뿐만 아니라 역직렬화가 새 개체를 다시 생성하는 것을 방지합니다 .

이 접근 방식은 Effective Java 작성자 Josh Bloch가 옹호합니다.

결론: 사용 권장

JDK에서 싱글톤 패턴 적용

JDK에서 java.lang.Runtime은 고전적인 싱글톤 모드(hungry Chinese style)입니다.아래와 같이 소스 코드를 살펴보겠습니다.
여기에 이미지 설명 삽입

요약하다

  1. 싱글톤 모드는 시스템 메모리에 이 클래스의 객체 하나만 존재하도록 하여 시스템 리소스를 절약합니다. 자주 생성 및 소멸되어야 하는 일부 객체의 경우 싱글톤 모드를 사용하면 시스템 성능을 향상시킬 수 있습니다.
  2. 싱글톤 클래스를 인스턴스화하려면 new가 아닌 ​​해당 메소드를 사용하여 객체를 가져오는 것을 기억해야 합니다.
  3. 싱글톤 패턴이 사용하는 시나리오: 자주 생성 및 파괴되어야 하는 객체, 생성하는 데 시간 또는 리소스 소모가 많은 객체(예: 중량 객체), 그러나 자주 사용하는 객체, 도구 객체, 빈번한 객체 데이터베이스 또는 파일에 접근하는 객체 (데이터 소스, 세션 팩토리 등)


추신: 내 개인 블로그로 이동하여 더 많은 콘텐츠를 볼 수도 있습니다.

개인 블로그 주소: Xiaoguan 동급생 블로그

추천

출처blog.csdn.net/weixin_45784666/article/details/120275606