单例模式的理解

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的理解,静态内部类的理解,枚举的理解,多线程的同步;

posted @ 2021-07-26 14:32  ethanSung  阅读(199)  评论(0编辑  收藏  举报