• 单例模式的理解


    1.单例模式

    概念:

    ​ 为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

    适用场景:

    1.1 单例的实现

    一,基本的实现方式:

    1. 静态化实例对象
    2. 私有化构造方法,禁止通过构造方法创建实例
    3. 提供一个公共的静态方法,用来返回唯一实例

    二,分类:

    实际开发过程中要考虑的点:

    以上几种分类需要关注三个方面进行选择使用:
    1.是否线程安全;
    2.最好是懒加载,避免资源的浪费;
    3.调用效率高一些,也就是涉及到同步的问题;

    方式一:饿汉式示例

    解析:

    ​ 饿汉式的特点是static变量,也就是该类的对象,在类加载时就会初始化生成该类对象。由于虚拟机在类加载初始化时候保证只加载一次该类,形成天然安全的线程安全环境,所以肯定不会发生并发访问的问题,因此不必加synchronized关键字来进行同步。以上两个特点会出现以下问题:

    1. 因为不需要同步,所以调用效率高;
    2. 因为类加载时直接生成了该类对象,如果没有调用该类,会造成资源的浪费;
    package com.ethan.singleton;
    /**
     * 1.饿汉式单例模式
     * @author ethan
     *
     */
    public class SingletonDemo1 {
    	/*
    	 * 	1.类初始化加载时就直接加载生成该类对象
    	 * 	2.线程安全,加载类时候天然的线程安全模式
    	 * 	3.不是延时加载对象,可能会造成资源的浪费
    	 */
    	
    	private static SingletonDemo1 instance = new SingletonDemo1();
    	private SingletonDemo1() {
    		
    	}
    	// 方法没有同步,调用效率高。
    	public static SingletonDemo1 getInstance() {
    		return instance;
    	}
    	
    }
    

    方式二:懒汉式示例

    解析:

    ​ 懒汉式的特点是延时加载生成该类的对象,必须调用getInstance()方法才会生成该类对象,真正用时候才会加载;但是可能会发生并发访问的问题,因此需要加synchronized关键字来进行同步。以上两个特点会出现以下问题:

    1. 因为需要同步,所以调用效率低;
    2. 用的时候才加载,不会造成资源的浪费;
    package com.ethan.singleton;
    /**
     * 2.懒汉式单例模式
     * @author ethan
     *
     */
    public class SingletonDemo2 {
    	//类初始化加载时,不初始化生成对象,延时加载生成该类对象,即真正使用对象时候在创建
    	private static volatile SingletonDemo2 instance = null;
    	private SingletonDemo2() {//私有化构造器
    		
    	}
    	// 方式一:方法需要同步,导致调用效率低!
    	public static synchronized SingletonDemo2 getinstance() {
    		if(instance == null) {
    			instance = new SingletonDemo2(); 
    		}
    		return instance;
    	}
    }
    

    方式三:双重检测锁式

    ​ 懒汉式因为同步,虽然线程安全,但是调用效率会低一些,有以下改进,利用双重检测对象是否为空,缩小同步代码块的范围,提高效率。

    package com.ethan.singleton;
    /**
     * 3.双重检测锁式
     * @author ethan
     *
     */
    public class SingletonDemo2 {
    	//类初始化加载时,不初始化生成对象,延时加载生成该类对象,即真正使用对象时候在创建
    	private static volatile SingletonDemo2 instance = null;
    	private SingletonDemo2() {//私有化构造器
    		
    	}
    	//双重检测,相比第二种方式,这种方式效率更好
        public static SingletonDemo2 getinstance() {
            if(instance == null) {
                synchronized (SingletonDemo2.class){
                    if (instance == null){
                        instance = new SingletonDemo2();
                    }
                }  
            }
            return instance;
        }
    }
    
    
    
    
    

    方式四:静态内部类式示例

    解析:

    ​ 外部类SingletonDemo4没有static变量,类加载时不会像饿汉式那样立即加载类对象;只有在真正调用getInstance()之后才会加载内部类,从而生成内部类的静态变量,也就是外部类的对象,因为加载类时候线程安全,instance是static final 类型,保证了内存中只有一个实例存在,而且只能被赋值一次,保证线程的安全性;兼备延时加载和并发安全,高效调用的优势;

    理解静态内部类的生成时机。

    package com.ethan.singleton;
    /**
     * 4.静态内部类实现单例模式
     * @author ethan
     *
     */
    public class SingletonDemo4 {
    	private static class SingletonDemo4Instance {
    		//final加不加都行
    		private static final SingletonDemo4 instance = new SingletonDemo4();
    	}
    	private SingletonDemo4(){
    		
    	}
    	public static SingletonDemo4 getInstance() {
    		return SingletonDemo4Instance.instance;
    	}
    }	
    

    方式五:枚举式示例

    解析:

    ​ 枚举类的对象天然是唯一的,那么创建一个枚举类,并且只有一个枚举值(枚举对象),那么这个枚举类就只能有一个对象,符合单例模式。避免了反射去生成其对象和序列化的漏洞,没有延时加载的效果。

    package com.ethan.singleton;
    
    public enum SingletonDemo5 {
    	//这个枚举元素,本身就是单例
    	INSTANCE;
    	
    	//添加自己需要的操作
    	public void singletonOperation() {
    		System.out.println("举例枚举单例的操作!!!");
    	}
    }
    
    
    

    2. 总结:

    选用标准

    1. 需要占用资源少,不需要延时加载 --->枚举好于饿汉式
    2. 占用资源大,需要延时加载 --->静态内部类式好于懒汉式

    2.1 回顾的知识点

    static的理解,final的理解,静态内部类的理解,枚举的理解,多线程的同步;

  • 相关阅读:
    CentOS7安装MySQL报错,解决Failed to start mysqld.service: Unit not found
    已知root用户密码并登录,修改mysql用户名密码方法
    修改完Apache的配置文件,重启Apache后,仍无法打开网页
    设置Apache(httpd)和Nginx 开机自启动
    Apache的网站,使用Nginx进行反向代理(1个IP绑定多个域名,对应多个网站)解决方案
    启动mysql遇到问题Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
    linux下启动mysql提示:Timeout error occurred trying to start MySQL Daemon
    在线上Linux下,PHP扩展安装(使用yum安装)
    在Linux下 MySQL错误 ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) 解决办法【很管用】
    mysql 如何删除数据库中所有的表
  • 原文地址:https://www.cnblogs.com/ethanSung/p/15061294.html
Copyright © 2020-2023  润新知