1,单例模式
1.1,单例的理解
单例模式是一种常用的软件设计模式,其定义的单例对象的类只能允许一个实例存在;
该类负责创建自己的对象,同时确保只有一个对象被创建;
一般常用在工具类的实现或创建对象需要消耗资源的业务场景。
1.2,单例的特点
类构造器私有;
持有自己类的引用;
对外提供获取实例的静态方法。
1.3,常用单例模式
饿汉模式:好处:没有线程安全;坏处:浪费内存空间。好处:没有线程安全问题
// 坏处:浪费内存空间好处好处:没有线程安全问题;坏处:浪费内存空间
懒汉模式:好处:没有内存空间的浪费;坏处:控制不好就是单例模式,比如多个线程执行时。
双重检查锁:既保证了线程安全(synchronized),又比直接上锁提高了执行效率(第一层校验),还节省了内存空间。
静态内部类:外呼类加载时不需要加载 静态内部类,不加载则不占用内存;当外部类调用 getInstance 方法时,才加载静态内部类(延迟加载),静态属性保证了全局唯一,静态变量初始化保证了线程安全,所以这里的方法没有加 synchronized 关键字(JVM保证了一个类的 初始化在多线程下被同步加锁)。
2,饿汉模式
public class SimpleSingleton {
private static final SimpleSingleton INSTANCE = new SimpleSingleton();
private SimpleSingleton(){}
public static SimpleSingleton getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
// 单例模式-饿汉模式
// 实例在初始化的时候就已经建好了,不管是否使用到,都已经实例化好了。
// 好处:没有线程安全问题
// 坏处:浪费内存空间
System.out.println(SimpleSingleton.getInstance().hashCode());
System.out.println(SimpleSingleton.getInstance().hashCode());
// 2084435065
// 2084435065
}
}
3,懒汉模式
public class SimpleSingleton2 {
// 懒汉模式
private static SimpleSingleton2 INSTANCE;
private SimpleSingleton2(){}
public static SimpleSingleton2 getInstance(){
if(null == INSTANCE){
return new SimpleSingleton2();
}
return INSTANCE;
}
public static void main(String[] args) {
// 懒汉模式
// 就是实例在用到的时候才创建,用的时候才去检查有无实例,有则返回,反之新建。
// 优点:没有内存空间的浪费
// 缺点:控制不好就不是单例模式,比如多个线程执行时
System.out.println(SimpleSingleton2.getInstance().hashCode());
System.out.println(SimpleSingleton2.getInstance().hashCode());
// 2084435065
// 1896277646
}
}
优化处理:
public class SimpleSingleton3 {
private static SimpleSingleton3 INSTANCE;
private SimpleSingleton3(){}
public synchronized static SimpleSingleton3 getInstance(){
if(null == INSTANCE){
return new SimpleSingleton3();
}
return INSTANCE;
}
public static void main(String[] args) {
// 改进懒汉模式,加 synchronized 关键字
// 使用 synchronized 关键字会消耗性能,我们应该判断INSTANCE为空时才加锁,
// 而不为空不应该加锁,需要直接返回。这就需要使用双重检查锁。
System.out.println(SimpleSingleton3.getInstance().hashCode());
System.out.println(SimpleSingleton3.getInstance().hashCode());
// 2084435065
// 1896277646
}
}
4,双重检查锁
public class SimpleSingleton4 {
private static SimpleSingleton4 INSTANCE;
private SimpleSingleton4(){}
// 双重检查锁
public static SimpleSingleton4 getInstance(){
if(null == INSTANCE){
synchronized (SimpleSingleton4.class){
if(null == INSTANCE){
INSTANCE = new SimpleSingleton4();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
// 双重检查锁
System.out.println(SimpleSingleton4.getInstance().hashCode());
System.out.println(SimpleSingleton4.getInstance().hashCode());
// 2084435065
// 2084435065
}
}
优化处理-双重检查锁-volatile:
public class SimpleSingleton5 {
// JVM虚拟机可能会重排 getInstance() 方法,所以需要添加关键字 volatile
// volatile 关键字可以保证多个线程的可见性,但不能保证原子性,同时它也能禁止指令重排。
// 可见性:即表现为 多个线程可同时读取同一变量时,且数据同步,共享结果
// 原子性:即表现为 该操作不可拆分,同一时刻只能有一个线程对它进行操作,不会被其他调度器所打断。
private volatile static SimpleSingleton5 INSTANCE;
private SimpleSingleton5(){}
public static SimpleSingleton5 getInstance(){
if(null == INSTANCE){
synchronized(SimpleSingleton5.class){
if(null == INSTANCE){
INSTANCE = new SimpleSingleton5();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
// 双重检查锁的机制 既保证了线程安全(synchronized),又比直接上锁提高了执行效率(第一层校验),还节省了内存空间。
System.out.println(SimpleSingleton5.getInstance().hashCode());
System.out.println(SimpleSingleton5.getInstance().hashCode());
// 2084435065
// 2084435065
}
}
5,静态内部类
public class SimpleSingleton6{
// 构造器私有
private SimpleSingleton6(){}
// 提供静态方法获取实例对象
public static SimpleSingleton6 getInstance(){
return Inner.INSTANCE;
}
// 保证单例对象,且静态变量保证唯一,线程安全
// 只有外呼类调用 getInstance 方法时,静态内部类才被加载,初始化变量,不占用内存
private static class Inner{
private static final SimpleSingleton6 INSTANCE = new SimpleSingleton6();
}
public static void main(String[] args){
System.out.println(SimpleSingleton6.getInstance().hashCode());
System.out.println(SimpleSingleton6.getInstance().hashCode());
// 2084435065
// 2084435065
}
}
示例:
public class SimpleSingleton6 {
private SimpleSingleton6(){}
public static SimpleSingleton6 getInstance(){
return Inner.INSTANCE;
}
private static class Inner{
private static final SimpleSingleton6 INSTANCE = new SimpleSingleton6();
}
public static void main(String[] args) {
// 静态内部类
// 在类中我们定义了一个静态的内部类 Inner,该类的 getInstance 方法返回的内部类 Inner 的实例 INSTANCE.
// 只有第一次调用 getInstance 方法时,虚拟机才加载 Inner 类并初始化 INSTANCE,
// 只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,保证对象的唯一性。
System.out.println(SimpleSingleton6.getInstance().hashCode());
System.out.println(SimpleSingleton6.getInstance().hashCode());
// 2084435065
// 2084435065
// 静态内部类的特点:
// 外部类加载时不需要加载 静态内部类,不被加载则不占用内存;
// 当外部类调用 getInstance 方法时,才加载静态内部类[延迟加载],静态属性保证了全局唯一,
// 静态变量初始化保证了线程安全,所以这里的方法没有加 synchronized 关键字(JVM保证了一个类的 初始化在多线程下被同步加锁)
}
}