单例模式的理解
1.单例模式
概念:
为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
适用场景:
1.1 单例的实现
一,基本的实现方式:
- 静态化实例对象
- 私有化构造方法,禁止通过构造方法创建实例
- 提供一个公共的静态方法,用来返回唯一实例
二,分类:
实际开发过程中要考虑的点:
以上几种分类需要关注三个方面进行选择使用:
1.是否线程安全;
2.最好是懒加载,避免资源的浪费;
3.调用效率高一些,也就是涉及到同步的问题;
方式一:饿汉式示例
解析:
饿汉式的特点是static变量,也就是该类的对象,在类加载时就会初始化生成该类对象。由于虚拟机在类加载初始化时候保证只加载一次该类,形成天然安全的线程安全环境,所以肯定不会发生并发访问的问题,因此不必加synchronized关键字来进行同步。以上两个特点会出现以下问题:
- 因为不需要同步,所以调用效率高;
- 因为类加载时直接生成了该类对象,如果没有调用该类,会造成资源的浪费;
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关键字来进行同步。以上两个特点会出现以下问题:
- 因为需要同步,所以调用效率低;
- 用的时候才加载,不会造成资源的浪费;
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. 总结:
选用标准
- 需要占用资源少,不需要延时加载 --->枚举好于饿汉式
- 占用资源大,需要延时加载 --->静态内部类式好于懒汉式
2.1 回顾的知识点
static的理解,final的理解,静态内部类的理解,枚举的理解,多线程的同步;