单例模式

定义:在一个jvm中只能存在一个实例,保证对象唯一性。

应用场景:servlet、struts2、springMVC、连接池、线程池、枚举、常量。

优点:节约内存、方便管理、重复利用。

缺点:线程不安全

单例本质上分为4类,分别是懒汉式,饿汉式,注册式和ThreadLocal式:

 

一、饿汉模式

1.金典款:类初始化的时候会立即创建该对象(存放在方法区中,不会被垃圾回收机制回收),线程天生安全(因为final修饰的全局变量),调用效率高,但是占内存:

/**
 * 优点:执行效率高,性能高,没有任何的锁
 * 缺点:某些情况下,可能会造成内存浪费
 */
public class Singleton {
    public static final Singleton signleton = new Singleton();
    private Singleton(){

    }

    public static Singleton getInstance(){
        return signleton;
    }
}

2.装逼款:

/*
    和经典款一个意思,就是专门写了个static块
*/
public class HungryStaticSingleton {
    //先静态后动态
    //先上,后下
    //先属性后方法
    private static final HungryStaticSingleton hungrySingleton;

    //装个B
    static {
        hungrySingleton = new HungryStaticSingleton();
    }

    private HungryStaticSingleton(){}

    public static HungryStaticSingleton getInstance(){
        return  hungrySingleton;
    }
}

 

二、懒汉模式

1.经典款:类初始化的时候不会创建该对象,只有当需要使用该对象的时候才会创建,具有懒加载功能,但是线程不安全需要自己加锁,所以效率低,会阻塞、等待:

/**
 * 优点:节省了内存,线程安全
 * 缺点:性能低
 */
public class Singleton {
    private static Singleton signleton;
    private Singleton(){

    }

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

 

2.静态内部类:                              

/*
   优点:写法优雅,利用了Java本身语法特点,性能高,避免了内存浪费,不能被反射破坏
   缺点:不优雅
 */
class Singleton {
    private Singleton() {
        if (SingletonInstance.singleton != null) {
            throw new RuntimeException("不允许非法访问");
        }
    }

    public static Singleton getInstance() {
        return SingletonInstance.singleton;
    }

    private static class SingletonInstance {
        public static final Singleton singleton = new Singleton();
    }

}

 

3.双重检验锁:因为jvm本质重排序,可能会导致多次初始化,所以要加上volatile关键字:                   

/**
 * 优点:性能高了,线程安全了
 * 缺点:可读性难度加大,不够优雅
 */
public class Singleton {
    public volatile static Singleton signleton;
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        if(signleton == null){
            synchronized (Singleton.class){
                if (signleton == null){
                    signleton = new Singleton();
                }
            }
        }
        return signleton;
    }
}

三:注册式

1.枚举单例:实现简单,调用效率高,枚举本身就是单例:                                                                                               

public enum EnumSingleton {
    INSTANCE;

    public static EnumSingleton getInstance(){return INSTANCE;}
}

2.容器单例

public class ContainerSingleton {
    private ContainerSingleton(){}

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

    public static Object getInstance(String className){
        Object instance = null;
        if(!ioc.containsKey(className)){
           try {
 		        synchronized (ContainerSingleton .class){
               		if(!ioc.containsKey(className)){
                		instance = Class.forName(className).newInstance();
               			ioc.put(className, instance);
			        }
            }catch (Exception e){
                e.printStackTrace();
            }
            return instance;
        }else{
            return ioc.get(className);
        }
    }
}

四:ThreadLocal式

/**
 * 在数据源切换的时候很有用
 */
public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocaLInstance.get();
    }
}

单例模式的核心思想就是控制创建实例对象,通过将构造方法私有化来防止多次创建对象的操作,但是有三种可能破坏单例,第一种就是通过反射,第二种是序列化与反序列化,第三种就是多线程。

防止反射破坏单例:通过反射机制来访问私有构造器,那么我们可以在私有构造器上加一个判断,如果对象已经被创建则抛出异常信息(或者其他处理),记得加锁,保证只能有一个访问对象。代码如下:

Class Sigleton{
    private static boolean FLAG = false;
    private Sigleton(){
          synchronied(Sigleton.Class){
            if(!FLAG){
                FLAG = !FLAG;

                //创建对象
            }ELSE{
                throw new RuntimeException("该对象是单例,不允许重复创建“);
            }
        }
    }
}

也可以使用枚举单例来解决这个问题,因为底层代码已经做了判断,如果是枚举类型,则不允许使用反射来创建枚举对象:

//在Constructor类中 
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");

防止序列化、反序列化破坏单例:

public class SeriableSingleton implements Serializable {


    //序列化
    //把内存中对象的状态转换为字节码的形式
    //把字节码通过IO输出流,写到磁盘上
    //永久保存下来,持久化

    //反序列化
    //将持久化的字节码内容,通过IO输入流读到内存中来
    //转化成一个Java对象


    public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}

    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }

    //只需要加上这个方法即可,主要是重写了这个方法,如果没有这个方法就会去调用newInstance()方法
    //底层源码: 
    /*Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null;
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
    */
    private Object readResolve(){ return INSTANCE;}

}

 

看了《设计模式之禅》发现了单例模式的一种扩展形式,可以限制指定生成对象的数量,个人理解是不是有点像“池”的概念,比如数据库连接池设置的最大连接数,扩展代码如下:

public class Singleton {
    //定义最多能产生的实例数量
    private static int maxNumOfSingleton = 2;
    //每个实例都有名字,使用一个ArrayList来容纳,每个对象的私有属性
    private static ArrayList<String> nameList = new ArrayList<String>();
    //定义一个列表,容纳所有的实例
    private static ArrayList<Singleton> emperorList = new ArrayList<Singleton>();
    //当前实例序列号
    private static int countNumOfSingleton = 0;
    //产生所有的对象
    static {
        for (int i = 0; i < maxNumOfSingleton; i++) {
            emperorList.add(new Singleton( (i + 1) + ""));
        }
    }
    private Singleton(String name){
        nameList.add(name);
    }

    public static Singleton getInstance(){
        Random random = new Random();
        //随机获取一个实例
        countNumOfSingleton = random.nextInt(maxNumOfSingleton);
        return emperorList.get(countNumOfSingleton);
    }

    public static void say() {
        System.out.println(nameList.get(countNumOfSingleton));
    }
}

这里可以随机指定一个实例进行处理,当然“池”的实现肯定比这个要复杂的多。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值