单例模式:一个类只有一个实例对象,提供一个全局的访问点
作用:保证一个类只有一个对象,降低对象之间的耦合度
原理:和饿汉式最大的区别就是实例创建的时机
饿汉式:创建时机不可控,在类加载的时候自动创建
原理:在同步锁的基础上,添加一层if判断,若单例已创建,就不需要在执行加锁操作就可以获取实例,从而提高性能
原理:根据静态内部类的特性,解决按需加载、线程安全,同时实现简洁
作用:保证一个类只有一个对象,降低对象之间的耦合度
单例的一般实现方法:
public class Singleton {
// 1、创建私有变量instance,用来记录Singleton的唯一实例
// 2、内部进行实例化
private static Singleton instance = new Singleton();
// 3、把类的构造方法私有化,不让外部调用构造方法
private Singleton() {
}
// 4、定义公共的方法提供该类的全局唯一访问点
// 5、外部通过调用getInstance()来返回唯一的实例
public static Singleton getInstance() {
return instance;
}
}
单例的实现方式可以分为2大类6种:饿汉式、枚举类型、懒汉式之基础实现、懒汉式之同步锁、懒汉式之双重检验锁、静态内部类
1、饿汉式
原理:依赖JVM类加载机制,保证单例只会被创建一次,即线程安全。
/**
* 饿汉式
* 应用场景:
* 要求初始化速度快,占用内存少
*/
class Singleton2 {
// 1、加载该类时,单例就会自动被创建
private static Singleton2 myInstance = new Singleton2();
// 2、构造函数设置为私有
public Singleton2() {
}
// 3、通过静态方法获得创建的单例
public static Singleton2 getMyInstance() {
return myInstance;
}
}
2、枚举类型原理:根据枚举的特点,满足单例模式所需的创建单例、线程安全、实现简洁的需求
/**
* 枚举实现
* 这是最简单、易用的单例实现方法
*/
enum Singleton3 {
// 定义一个 枚举,即为单例的一个实例
INSTANCE;
// 隐藏一个空的构造方法
private Singleton3() {}
}
//获取单例的方式
Singleton3 singleton3 = Singleton3.INSTANCE;
3、懒汉式之基础实现原理:和饿汉式最大的区别就是实例创建的时机
饿汉式:创建时机不可控,在类加载的时候自动创建
懒汉式:在有需求的时候手动创建
/**
* 懒汉式(基础实现)
*/
class Singleton4 {
// 1、类加载时,先不自动创建单例
private static Singleton4 myInstance = null;
// 2、构造函数
private Singleton4() {}
// 3、需要时才手动调用newInstance()创建
public static Singleton4 newInstance() {
// 先判断单例是否为空,避免重复创建
if (myInstance == null) {
myInstance = new Singleton4();
}
return myInstance;
}
}
缺点:线程不安全
4、懒汉式之同步锁
原理:使用同步锁synchronized锁住创建单例的方法,防止多个线程同时调用,从而避免造成单例的多次被创建
/**
* 懒汉式之同步锁
* 线程安全
* 缺点:每次访问都需要进行线程同步(调用synchronized),造成过多的同步开销
*/
class Singleton5 {
private static Singleton5 myInstance = null;
private Singleton5() {}
public static Singleton5 newInstance() {
synchronized (Singleton5.class) {
if (myInstance == null) {
myInstance = new Singleton5();
}
}
return myInstance;
}
}
5、懒汉式之双重检验锁原理:在同步锁的基础上,添加一层if判断,若单例已创建,就不需要在执行加锁操作就可以获取实例,从而提高性能
/**
* 懒汉式之双重校验
注意:多重判断,容易出错
*/
class Singleton6 {
private static Singleton6 myInstance = null;
public Singleton6() {
}
public static Singleton6 newInstance() {
if ( myInstance == null) {
synchronized(Singleton6.class) {
if (myInstance == null) {
myInstance = new Singleton6();
}
}
}
return myInstance;
}
}
6、静态内部类原理:根据静态内部类的特性,解决按需加载、线程安全,同时实现简洁
/**
* 静态内部类
* 调用过程:
* 1、外部调用类的newInstance()
* 2、自动调用SIngleton7.newInstance()
* 此时单例类Singleton8得到初始化
* 而该类在装载 & 被初始化时,会初始化它的静态域,从而创建单例;
* 由于是静态域,因此只会JVM只会加载1遍,Java虚拟机保证了线程安全性
* 3、最终只创建一个单例
*/
class Singleton7 {
// 1、创建静态内部类
private static class Singleton8 {
// 在静态内部类里创建单例
private static Singleton7 myInstance = new Singleton7();
}
private Singleton7() {}
public static Singleton7 newInstance() {
return Singleton8.myInstance;
}
}
单例模式需要注意些什么?
构造函数要私有化
含有一个该类的静态私有对象
有一个静态的公有的函数用于创建或获取他本身的静态私有对象
需要考虑到线程同步问题