java设计模式之单例模式

单例模式的定义:

  单例模式指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,属于创建型设计模式。

单例模式的应用场景:

  • 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少GC。
  • 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  • 频繁访问数据库或者文件的对象。

  实际运用中,J2EE标准中的ServletContext和ServletContextConfig、Spring框架应用中的ApplicationContext、Spring中创建的默认bean、数据库连接池、

全局线程池等等都是单例模式。

单例模式的几种实现方式:

1,饿汉式

package singleton;

/**
 * @ClassName SingletonDemo1
 * @Description 饿汉式
 * 类加载到内存后,就实例化一个单例对象,JVM保证线程安全
 * 唯一缺点:不管用不用都会进行加载,但是影响不大,实际上是最适用的一种方式
 * @Author liuyi
 * @Date 2020/6/7 12:22
 * @Version 1.0
 */
public class SingletonDemo1 {
    //两种方式初始化实例,两种方式的效果是一样的
    静态常量方式
    private static final SingletonDemo1 instance = new SingletonDemo1();
    静态块方式
    private static final SingletonDemo1 instance;
    static {
        instance = new SingletonDemo1();
    }
    private SingletonDemo1(){

    }
     SingletonDemo1 getInstance(){
        return instance;
    }

    void main(String[] args) {
        SingletonDemo1 instance1 = SingletonDemo1.getInstance();
        SingletonDemo1 instance2 = SingletonDemo1.getInstance();
        System.out.println(instance1==instance2);
        返回的结果为true,说明不管取多少次都是同一个实例
    }
}

2,懒汉式


 * @ClassName SingletonDemo2
 * @Description 懒汉式
 * 需要使用该对象的时候再去实例化
 * 缺点:会产生线程安全问题
 * @Author liuyi
 * @Date 2020/6/7 13:23
 * @Version 1.0
  SingletonDemo2 {
     SingletonDemo2 instance;

     SingletonDemo2(){

    }

     SingletonDemo2 getInstance(){
        if(instance==null){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance =  SingletonDemo2();
        }
         main(String[] args) {
        为什么说线程不安全
        因为使用了全局的静态变量,会造成多线程访问的时候会产生不唯一的实例
        for (int i = 0; i <100 ; i++) {
            new Thread(()->{
                System.out.println(getInstance());
            }).start();
        }
        打印的对象完全乱了,根本不是同一个实例
    }
}

3,饿汉式(加锁)


 * @ClassName SingletonDemo2
 * @Description 懒汉式(加锁)
 * 缺点:效率低
 * @Author liuyi
 * @Date 2020/6/7 13:23
 * @Version 1.0
  SingletonDemo3 {
     SingletonDemo3 instance;

     SingletonDemo3(){

    }

    synchronized  SingletonDemo3 getInstance(){
        ){
            instance =  SingletonDemo3();
        }
        加了锁解决了懒汉式的线程不安全问题,但是这样效率就会明显降低
        {
                System.out.println(getInstance());
            }).start();
        }
    }
}

4,双重检查


 * @ClassName SingletonDemo2
 * @Description 双重检查(比较完美的写法)
 * @Author liuyi
 * @Date 2020/6/7 13:23
 * @Version 1.0
  SingletonDemo4 {
    必须加volatile关键字,防止指令重排
    volatile SingletonDemo4 instance;

     SingletonDemo4(){

    }

     SingletonDemo4 getInstance(){
        为什么要进行双重检查
        比如两个线程同时进入该方法,都拿到instance为空,其中一个拿到锁并new了一个实例,
        此时另外一个线程它并不知道你已经new了实例,所以当它拿到锁之后会继续new一个实例
        所以如果在锁里面继续判断一次是很有必须要的
        synchronized (SingletonDemo4.){
                ){
                    instance =  SingletonDemo4();
                }
            }
        }
        {
                System.out.println(getInstance());
            }).start();
        }
    }
}

5,静态内部类方式


package com.liuyi.entity;

/**
* @ClassName SingletonDemo5
* @Description 静态内部类方式
* JVM保证线程安全
* 加载外部类是不会加载内部类,实现了懒加载
* 最完美的写法
* @Author liuyi
* @Date 2020/6/7 13:52
* @Version 1.0
*/
public class SingletonDemo5 {

private SingletonDemo5(){
//防止恶意通过反射破坏单例
     if(SingletonDemo5Inside.instance!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
private static class SingletonDemo5Inside{
private static final SingletonDemo5 instance = new SingletonDemo5();
}

public static SingletonDemo5 getInstance(){
return SingletonDemo5Inside.instance;
}

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

6,枚举方式


 * @Author liuyi
 * @Description 枚举单例
 * 不仅可以解决线程同步,还可以防止反序列化(因为枚举类没有构造方法)
 * @Date 17:28 2020/6/7
 * @Param  
 * @return 
 *enum SingletonDemo6 {

    instance;

     test(){
        System.out.println("测试测试");
    }
     main(String[] args) {
        instance.test();
        {
                System.out.println(SingletonDemo6.instance);
            }).start();
        }
    }
}

容器式单例解决大规模生产单例问题:

 com.gupaoedu.vip.pattern.singleton.register;

import java.util.Map;
 java.util.concurrent.ConcurrentHashMap;


 * 大规模生产单例
  ContainerSingleton {

     ContainerSingleton(){}

    static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();

    synchronized Object getInstance(String className){
        Object instance = ;
        判断集合中是否有当前对象
        if(!ioc.containsKey(className)){
             {
                如果没有,通过反射创建对象,并放入map集合中
                instance = Class.forName(className).newInstance();
                ioc.put(className,instance);
            } (Exception e){
                e.printStackTrace();
            }
             instance;
        }else{
            如果集合中有,则直接取出来返回
             ioc.get(className);
        }
    }

}

ThreadLocal单例(线程内单例):

 ThreadLocal不能保证创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,是线程安全的。

 com.gupaoedu.vip.pattern.singleton.threadlocal;


 * ThreadLocal实现线程内单例
  ThreadLocalSingleton {
    final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return  ThreadLocalSingleton();
                }
            };

     ThreadLocalSingleton(){}

     ThreadLocalSingleton getInstance(){
         threadLocaLInstance.get();
    }
}

单例模式的优点:

  • 单例模式可以保证内存中只有一个实例,减少内存的开销。
  • 单例模式可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

单例模式的缺点:

  • 单例模式一般没有接口,扩展困难,违背开闭原则。
  • 单例模式的代码通常写在一个类中,如果设计不合理,则很容易违背单一职责原则。

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结