"디자인 패턴" 싱글톤 패턴

일부 시스템에서는 메모리 리소스를 절약하고 데이터 내용의 일관성을 보장하기 위해 특정 클래스의 인스턴스 하나만 생성하면 되는데, 이것이 소위 싱글톤 모드입니다.

1. 싱글톤 패턴의 정의와 특징

싱글톤() 패턴의 정의: 클래스에 인스턴스가 하나만 있고 클래스가 이 인스턴스를 스스로 생성할 수 있는 패턴을 말합니다. 예를 들어, Windows에서는 하나의 작업 관리자만 열 수 있으므로 여러 작업 관리자 창을 열 때 발생하는 메모리 리소스 낭비나 각 창의 표시 내용이 일치하지 않는 등의 오류를 방지할 수 있습니다.

컴퓨터 시스템에는 Windows 휴지통, 운영 체제의 파일 시스템, 멀티스레딩 의 스레드 풀 , 그래픽 카드의 드라이버 개체, 프린터의 백그라운드 처리 서비스, 로그 개체 도 있습니다. 애플리케이션, 데이터베이스의 연결 풀 및 웹 사이트 카운터, 웹 애플리케이션의 구성 개체, 애플리케이션의 대화 상자, 시스템의 캐시 등은 종종 싱글톤으로 설계됩니다.

싱글톤 패턴에는 세 가지 특성이 있습니다.

  1. 싱글톤 클래스에는 인스턴스 객체가 하나만 있습니다.
  2. 싱글톤 객체는 싱글톤 클래스 자체에서 생성되어야 합니다.
  3. 싱글톤 클래스는 싱글톤에 대한 전역 액세스 지점을 제공합니다.

2. 싱글톤 패턴의 구조와 구현

싱글톤 패턴은 가장 단순한 디자인 패턴 중 하나입니다. 일반적으로 일반 클래스의 생성자는 public이고, 외부 클래스는 "new constructor()"를 통해 여러 인스턴스를 생성할 수 있습니다. 그러나 클래스의 생성자를 비공개로 설정하면 외부 클래스는 생성자를 호출할 수 없으며 여러 인스턴스를 생성할 수 없습니다. 이때, 클래스 자체는 정적 프라이빗 인스턴스를 정의하고, 정적 프라이빗 인스턴스를 생성하거나 획득하기 위한 정적 공용 함수를 제공해야 합니다.

기본 구조와 구현 방법을 분석해 보겠습니다.

2.1 싱글톤 패턴의 구조

싱글턴 패턴의 주요 역할은 다음과 같습니다.

  • 싱글톤 클래스: 하나의 인스턴스를 포함하고 이 인스턴스를 자체적으로 생성할 수 있는 클래스입니다.
  • 액세스 클래스: 싱글톤을 사용하는 클래스입니다.

그 구조는 그림 1에 나와 있습니다.

2.2 싱글톤 패턴 구현

일반적으로 싱글톤 패턴에는 두 가지 구현이 있습니다.

2.3.1 게으른 싱글톤

이 모드의 특징은 클래스가 로드될 때 싱글턴이 생성되지 않는다는 점이며, 이 싱글턴은 getInstance 메소드가 처음 호출될 때만 생성됩니다. 코드는 아래와 같이 표시됩니다.

public class LazySingleton
{
    private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步
    private LazySingleton(){}    //private 避免类在外部被实例化
    public static synchronized LazySingleton getInstance()
    {
        //getInstance 方法前加同步
        if(instance==null)
        {
            instance=new LazySingleton();
        }
        return instance;
    }
}

참고: 다중 스레드 프로그램을 작성하는 경우 위 예제 코드에서 휘발성 및 동기화 키워드를 삭제하지 마십시오. 그렇지 않으면 스레드 비안전 문제가 발생합니다. 이 두 키워드를 삭제하지 않으면 스레드 안전성은 보장할 수 있지만 접근할 때마다 동기화가 필요해 성능에 영향을 미치고 더 많은 리소스를 소모하게 된다는 게 게으른 싱글턴의 단점이다.

2.3.2 Hungry Han 스타일 싱글톤

이 모드의 특징은 클래스가 로드되면 싱글톤이 생성되어 getInstance 메소드가 호출되기 전에 싱글톤이 이미 존재하는지 확인한다는 것입니다.

public class HungrySingleton
{
    private static final HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance()
    {
        return instance;
    }
}

Hungry 스타일의 싱글턴은 클래스 생성 시 시스템용 정적 객체를 이미 생성해 두었고 앞으로도 변경하지 않으므로 스레드로부터 안전하며 멀티스레딩에서도 문제 없이 바로 사용할 수 있다.

3. 싱글톤 모드를 작성하는 8가지 방법

3.1 Hungry Han 스타일 싱글톤

작성 방법 1: 

package com.company.singleton;

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用!
 * 唯一缺点:不管用到与否,类装载时就完成实例化
 * (话说你不用的,你装载它干啥)
 */
public class Mgr01 {

    private static final Mgr01 INSTANCE = new Mgr01();

    private Mgr01() {};

    public static Mgr01 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        Mgr01 m1 = Mgr01.getInstance();
        Mgr01 m2 = Mgr01.getInstance();
        System.out.println(m1 == m2);
    }
}

작성 방법 2:

package com.company.singleton;

/**
 * 跟01是一个意思
 */
public class Mgr02 {
    private static final Mgr02 INSTANCE;
    static {
        INSTANCE = new Mgr02();
    }

    private Mgr02() {};

    public static Mgr02 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        Mgr02 m1 = Mgr02.getInstance();
        Mgr02 m2 = Mgr02.getInstance();
        System.out.println(m1 == m2);
    }
}

3.2 게으른 싱글톤

작성 방법 1;

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 */
public class Mgr03 {
    private static Mgr03 INSTANCE;

    private Mgr03() {
    }

    public static Mgr03 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->
                System.out.println(Mgr03.getInstance().hashCode())
            ).start();
        }
    }
}

작성 방법 2;

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr04 {
    private static Mgr04 INSTANCE;

    private Mgr04() {
    }

    public static synchronized Mgr04 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr04.getInstance().hashCode());
            }).start();
        }
    }
}

작성 방법 3:

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr05 {
    private static Mgr05 INSTANCE;

    private Mgr05() {
    }

    public static Mgr05 getInstance() {
        if (INSTANCE == null) {
            //妄图通过减小同步代码块的方式提高效率,然后不可行
            synchronized (Mgr05.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr05();
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr05.getInstance().hashCode());
            }).start();
        }
    }
}

작성 방법 4:

package com.company.singleton;

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr06 {
    private static volatile Mgr06 INSTANCE; //JIT

    private Mgr06() {
    }

    public static Mgr06 getInstance() {
        if (INSTANCE == null) {
            //双重检查
            synchronized (Mgr06.class) {
                if(INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr06();
                }
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr06.getInstance().hashCode());
            }).start();
        }
    }
}

작성 방법 5:

package com.company.singleton;

/**
 * 静态内部类方式
 * JVM保证单例
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class Mgr07 {

    private Mgr07() {
    }

    private static class Mgr07Holder {
        private final static Mgr07 INSTANCE = new Mgr07();
    }

    public static Mgr07 getInstance() {
        return Mgr07Holder.INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for(int i=0; i<1000; i++) {
            new Thread(()->{
                System.out.println(Mgr07.getInstance().hashCode());
            }).start();
        }
    }


}

작성 방법 6:

package com.company.singleton;

/**
 * 不仅可以解决线程同步,还可以防止反序列化。
 */
public enum Mgr08 {

    INSTANCE;

    public void m() {}

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Mgr08.INSTANCE.hashCode());
            }).start();
        }
    }

}

4. 싱글턴 패턴 적용 예시

[예제 1] 게으른 싱글톤 패턴을 사용하여 미국 현직 대통령 객체의 생성을 시뮬레이션합니다.

분석: 각 임기 동안 미국 대통령은 단 한 명이므로 싱글톤 모델을 구현하는 데 적합하다. 그림 2는 게으른 스타일 싱글톤을 사용하여 구현한 구조도를 보여준다.

그림 2 미국 대통령 생성기의 구조도 

프로그램 코드는 다음과 같습니다.

public class SingletonLazy
{
    public static void main(String[] args)
    {
        President zt1=President.getInstance();
        zt1.getName();    //输出总统的名字
        President zt2=President.getInstance();
        zt2.getName();    //输出总统的名字
        if(zt1==zt2)
        {
           System.out.println("他们是同一人!");
        }
        else
        {
           System.out.println("他们不是同一人!");
        }
    }
}
class President
{
    private static volatile President instance=null;    //保证instance在所有线程中同步
    //private避免类在外部被实例化
    private President()
    {
        System.out.println("产生一个总统!");
    }
    public static synchronized President getInstance()
    {
        //在getInstance方法上加同步
        if(instance==null)
        {
               instance=new President();
        }
        else
        {
           System.out.println("已经有一个总统,不能产生新总统!");
        }
        return instance;
    }
    public void getName()
    {
        System.out.println("我是美国总统:特朗普。");
    }  
}

프로그램을 실행한 결과는 다음과 같습니다.

대통령을 만들어라!
나는 미국 대통령이다: 트럼프.
이미 대통령이 있는데 새로운 대통령을 배출할 수 없습니다!
나는 미국 대통령이다: 트럼프.
그들은 같은 사람입니다!

[예제 2] Hungry 스타일 싱글톤 모드를 사용하여 Zhubajie 객체 생성을 시뮬레이션합니다.

분석: 위의 예와 유사하게 Zhu Bajie가 하나만 있으므로 이 예는 싱글톤 모드 구현에도 적합합니다. 이 예제에서는 Zhu Bajie의 이미지를 표시할 것이므로( 프로그램에서 표시할 Zhu Bajie의 이미지를 다운로드하려면 여기를 클릭하십시오 ) 프레임 형태의 JFrame 컴포넌트를 사용합니다.여기서 Zhu Bajie 클래스는 싱글톤 클래스이며, 패널 JPanel의 하위 클래스로 정의할 수 있으며 Zhu Bajie 이미지를 저장하기 위한 태그가 포함되어 있으며 클라이언트 양식은 Zhu Bajie 개체를 가져와 표시할 수 있습니다. 그림 3은 Hungry 스타일 싱글톤을 사용하여 구현된 구조 다이어그램을 보여줍니다.

그림 3 Zhubajie 발전기의 구조도

프로그램 코드는 다음과 같습니다.

import java.awt.*;
import javax.swing.*;
public class SingletonEager
{
    public static void main(String[] args)
    {
        JFrame jf=new JFrame("饿汉单例模式测试");
        jf.setLayout(new GridLayout(1,2));
        Container contentPane=jf.getContentPane();
        Bajie obj1=Bajie.getInstance();
        contentPane.add(obj1);    
        Bajie obj2=Bajie.getInstance(); 
        contentPane.add(obj2);
        if(obj1==obj2)
        {
            System.out.println("他们是同一人!");
        }
        else
        {
            System.out.println("他们不是同一人!");
        }   
        jf.pack();       
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
class Bajie extends JPanel
{
    private static Bajie instance=new Bajie();
    private Bajie()
    { 
        JLabel l1=new JLabel(new ImageIcon("src/Bajie.jpg"));
        this.add(l1);   
    }
    public static Bajie getInstance()
    {
        return instance;
    }
}

프로그램 실행 결과는 그림 4에 나와 있습니다.

그림 4 Zhubajie 발전기의 실행 결과

5. 싱글톤 모드의 응용 시나리오 

싱글턴 패턴의 구조와 특성은 앞서 분석한 바 있으며, 일반적으로 적용되는 시나리오의 특성은 다음과 같다.

  • 애플리케이션 시나리오에서 특정 유형이 클래스 모니터, 각 개인의 ID 번호 등과 같은 하나의 개체 생성만 필요한 경우
  • 객체를 공유해야 하는 경우. 싱글톤 패턴에서는 하나의 객체만 생성할 수 있으므로 객체를 공유하면 메모리를 절약하고 객체 액세스 속도를 높일 수 있습니다. 웹의 구성 개체, 데이터베이스 연결 풀 등
  • 멀티스레드 스레드 풀, 네트워크 연결 풀 등 특정 클래스를 자주 인스턴스화해야 하고 생성된 객체가 자주 소멸되는 경우

6. 싱글톤 패턴의 확장

싱글톤 모드는 제한된 다중 인스턴스(Multitcm) 모드로 확장될 수 있으며, 이 모드에서는 제한된 수의 인스턴스를 생성하여 ArrayList에 저장할 수 있으며 필요할 때 고객이 무작위로 얻을 수 있습니다. 구조 다이어그램은 그림 5에 나와 있습니다. .

그림 5 제한된 다중 인스턴스 패턴의 구조 다이어그램 

참고글 :  싱글톤 패턴에 대한 자세한 설명(싱글케이스 디자인 패턴)_smile_and_ovo 블로그-CSDN blog_singlecasepattern

Supongo que te gusta

Origin blog.csdn.net/m0_50370837/article/details/126179908
Recomendado
Clasificación