写在前面
单例模式定义: 一个类 只能创建一个实例。
使用一个对象来做就不用实例化多个对象!这就能减少我们空间和内存的开销~
我们使用静态类.doSomething()和使用单例对象调用方法的效果是一样的啊。
没错,效果就是一样的。使用静态类.doSomething()体现的是基于对象,而使用单例设计模式体现的是面向对象。
编写单例模式的代码其实很简单,就分了三步:
将构造函数私有化
在类的内部创建实例
提供获取唯一实例的方法
常用的单例模式
- 饿汉式
public class Liuge36 {
// 1.构造函数私有化
private Liuge36(){
}
// 2.在类的内部创建实例
private static Liuge36 liuge36 = new Liuge36();
// 3.提供获取实例的唯一方法
public static Liuge36 getInstance(){
return liuge36;
}
}
一上来就创建对象了,如果该实例从始至终都没被使用过,则会造成内存浪费。
- 简单懒汉式 (在方法上加锁)
既然说一上来就创建对象,如果没有用过会造成内存浪费:
那么我们就设计用到的时候再创建对象!
public class Liuge36 {
// 1.将构造函数私有化
private Liuge36(){
}
// 2.先不创建对象,等用到的时候 ,再开始创建
private static Liuge36 liuge36 = null;
/**
* 3.调用获取实例的方法,说明要用到实例对象了
* synchronized 加锁之后才能保证在多线程下代码可用,不加锁仅仅适用于单线程的情况
*/
public static synchronized Liuge36 getInstance(){
// 先判断对象是否为空,如果为空,先创建再返回出去
if (liuge36 == null) {
liuge36 = new Liuge36();
}
return liuge36;
}
}
- DCL双重检测加锁(进阶懒汉式)
上面那种直接在方法上加锁的方式其实不够好,因为在方法上加了内置锁在多线程环境下性能会比较低下,所以我们可以将锁的范围缩小。
public class Liuge36 {
// 1.将构造函数私有化
private Liuge36(){
}
/**
* 利用静态变量liuge36来记录Liuge36的唯一实例
*
* volatile 关键字确保:当变量liuge36 被初始化成 Liuge36实例时,
* 多个线程正确地处理liuge36变量
*
* volatile有内存屏障的功能!
*/
private static volatile Liuge36 liuge36 = null;
// 提供获取唯一实例的方法
public static Liuge36 getInstance(){
if (liuge36 == null){
// 同步代码块 ,将锁的范围缩小,提高性能
synchronized (Liuge36.class){
// 再次判断对象是否创建过
if (liuge36 == null){
liuge36 = new Liuge36();
}
}
}
return liuge36;
}
}
- 静态内部类实现懒汉式
public class Liuge36 {
// 1.将构造函数私有化
private Liuge36(){
}
/**
* 使用静态内部类的方式实现懒加载
* 初始化静态数据时,Java提供了的线程安全性保证。(所以不需要任何的同步)
*/
private static class LazyHolder{
// 创建单例对象
private static final Liuge36 instance = new Liuge36();
}
// 提供获取实例的唯一方法
public static Liuge36 getInstance(){
return LazyHolder.instance;
}
}
- 枚举方式
Joshua Bloch说“单元素的枚举类型已经成为实现Singleton的最佳方法
public class Liuge36 {
// 1.将构造函数私有化
private Liuge36(){
}
// 2 定义一个静态枚举类
static enum SingletonEnum{
INSTANCE;
private Liuge36 liuge36;
// 私有化枚举的构造函数
private SingletonEnum(){
liuge36 = new Liuge36();
}
public Liuge36 getLiuge36(){
return liuge36;
}
}
// 3. 提供获取实例的唯一方法
public static Liuge36 getInstance(){
return SingletonEnum.INSTANCE.getLiuge36();
}
public static void main(String [] args){
System.out.println(Liuge36.getInstance());
System.out.println(Liuge36.getInstance());
System.out.println(Liuge36.getInstance()==Liuge36.getInstance());
}
}
总的来说单例模式写法有5种:
饿汉式
简单懒汉式(在方法加锁)
DCL双重检测加锁(进阶懒汉式)
静态内部类实现懒汉式(最推荐写法)
枚举方式(最安全、简洁写法)
参考:java3y & https://www.jianshu.com/p/d35f244f3770