首页 > 其他 > 详细

单例模式

时间:2021-02-19 17:03:59      阅读:28      评论:0      收藏:0      [点我收藏+]

单例模式

1.概念

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

技术分享图片

 

 

单例的实现主要是通过以下两个步骤

  1. 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
  2. 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

2、应用场景

  举一个小例子,在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。

  继续说回收站,我们在实际使用中并不存在需要同时打开两个回收站窗口的必要性。假如我每次创建回收站时都需要消耗大量的资源,而每个回收站之间资源是共享的,那么在没有必要多次重复创建该实例的情况下,创建了多个实例,这样做就会给系统造成不必要的负担,造成资源浪费。

  再举一个例子,网站的计数器,一般也是采用单例模式实现,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题。同样多线程的线程池的设计一般也是采用单例模式,这是由于线程池需要方便对池中的线程进行控制

  同样,对于一些应用程序的日志应用,或者web开发中读取配置文件都适合使用单例模式,如HttpApplication 就是单例的典型应用。

  从上述的例子中我们可以总结出适合使用单例模式的场景和优缺点:  

   适用场景:

  • 1.需要生成唯一序列的环境
  • 2.需要频繁实例化然后销毁的对象。
  • 3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
  • 4.方便资源相互通信的环境
  • 5.Windows的任务管理器
  • 6.Windows的回收站
  • 7.项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
  • 网站的计数器一般也会采用单例模式,可以保证同步
  • 在Servlet编程中,每个Servlet也是单例的
  • 在Spring中,每个Bean默认就是单例的
  • ........

3、单例模式的优缺点

优点

  • 在内存中只有一个对象,节省内存空间;

  • 避免频繁的创建销毁对象,可以提高性能;

  • 避免对共享资源的多重占用,简化访问;

  • 为整个系统提供一个全局访问点。

缺点

  •  不适用于变化频繁的对象;

  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;

  • 如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;

4、单例模式的实现

严格来说有八种。只有两种是完美的

1.饿汉式

类加载到内存后,就实例化一个单例,JVM保证线程安全

简单实用,推荐使用!

唯一缺点:不管用到与否,类装载时就完成实例化(话说你不用,你装在它干啥)

 1 public class Way01 {
 2     private static final Way01 INSTANCE=new Way01();
 3 
 4     private Way01() {}
 5 
 6     public Way01 getInstance(){
 7         return INSTANCE;
 8     }
 9
//测试一下
10 public static void main(String[] args) { 11 Way01 w1=new Way01().getInstance(); 12 Way01 w2=new Way01().getInstance(); 13 System.out.println(w1==w2); 14  } 15 }

2.与第一种类似。

public class Way02 {
    private static Way02 INSTANCE;

    private Way02(){}
    static {
        INSTANCE = new Way02();
    }

    public Way02 getInstance() {
        return INSTANCE;
    }
//测试一下
    public static void main(String[] args) {
        Way02 w1 = new Way02().getInstance();
        Way02 w2 = new Way02().getInstance();
        System.out.println(w1 == w2);
    }
}

3.懒汉式

虽然达到了按需初始化的目的。但却带来了线程不安全的问题

public class Way03 {
    private static  Way03 INSTANCE;

    private Way03(){}

    public static Way03 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE=new Way03();
        }
        return INSTANCE;
    }
//测试一下
public static void main(String[] args) { for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(Way03.getInstance().hashCode()); }).start(); } } }

4,懒汉式  

虽然达到了按需初始化的目的。但却带来了线程不安全的问题

用过synchronized解决,但也带来了效率的降低

public class Way04 {
    private static  Way04 INSTANCE;

    private Way04(){}

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

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

5.懒汉式

减小同步代码块,为提高效率,但是依旧线程不安全

public class Way05 {
    private static  Way05 INSTANCE;

    private Way05(){}

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

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

6.双检索

public class Way06 {
    private static  Way06 INSTANCE;

    private Way06(){}

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

            }
        }
        return INSTANCE;
    }

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

7.静态内部类方式

JVM保证单例

加载外部类时不会加载内部类。这样可以实现懒加载。

public class Way07 {

    private Way07(){}

    public static class Way07Holder{
        private  final static Way07 INSTANCE=new Way07();
    }

    public Way07 getInstance(){
        return Way07Holder.INSTANCE;
    }

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

8.枚举

不仅可以解决线程同步,还可以防止序列化

public enum  Way08 {
    INSTANCE;

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

}

 

单例模式

原文:https://www.cnblogs.com/jingge0723/p/14416363.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!