文章目录
前言
设计模式记录学习
一、什么是单例模式?
单例模式是java中最简单的设计模式之一,这种类型的设计模式属于创建类型的一种,它提供了创建对象的最佳方式。
就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供了一个取得其对象实例的静态方法。
例如Hibernate的sessionFactory 它充当数据存储源代理,并且负责创建Session对象.SessionFactory并不是轻量级,一般情况下,一个项目只需要一个SessionFactory,这时就会用到单例模式
二、单例模式的8种写法
1.饿汉式(静态常量)
优点: 写法简单 类装载的是时候就完成实例化,避免了线程同步的问题
缺点:在类装载的时候完成实例化没,没有达到lazy loading (懒加载)的效果,如果从始至终没有使用过这个实例,则会造成内存浪费
package com.good.app.singleton.type1;
import lombok.Getter;
import org.junit.jupiter.api.Test;
/**
* 单例模式 1 饿汉式
* 优点:写法简单 类装载的是时候就完成实例化,避免了线程同步的问题
* 缺点:在类装载的时候完成实例化没,没有达到lazy loading (懒加载)的效果,如果从始至终没有使用过这个实例,则会造成内存浪费
*/
public class Singleton1 {
@Getter
private static final Singleton1 instance = new Singleton1();
//私有化构造器
private Singleton1() {
}
@Test
public void test(){
Singleton1 instance1 = Singleton1.getInstance();
Singleton1 instance2 = Singleton1.getInstance();
System.out.println(instance2==instance1);
System.out.println("instance1.hashCode() = " + instance1.hashCode());
System.out.println("instance2.hashCode() = " + instance2.hashCode());
}
}
2.饿汉式(静态代码块)
优点 类装载的是时候就完成实例化,避免了线程同步的问题
缺点 可能存在内存浪费
这种方式和上面的方式其实类似,只不过将类实例化的过程放到了静态代码块种,也是在类装载的时候,就是执行静态代码块种的代码,初始化类的实例
package com.good.app.singleton.type1;
import lombok.Getter;
/**
* 单例模式2 懒汉式(静态代码块)
* 优点 类装载的是时候就完成实例化,避免了线程同步的问题
* 缺点 可能存在内存浪费
*/
public class Singleton2 {
@Getter
private static final Singleton2 instance;
//在静态代码块中 创建单例对象
static {
instance = new Singleton2();
}
//私有化构造器
private Singleton2(){
}
}
3.懒汉式(线程不安全)
提供一个静态的公有方法,当使用到该方法的时候,才去创建实例
优点: 起到了延迟加载的效果,但是只能在单线程下使用
在实际开发种不推荐这种方式
package com.good.app.singleton.type1;
//懒汉式 延迟加载 在使用时候 实例化对象 有线程安全问题
public class Singleton3 {
private static Singleton3 instance = null;
private Singleton3() {
}
/**
* 在if 判断 为真 需要new 实例对象的时候有另外的线程进来 也是为真的 这时候就会再次走到 new 实例的 逻辑中
*
*/
public static Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
4.懒汉式(线程安全,同步方法)
优缺点说明:
优点: 解决了线程不安全的问题
缺点: 效率太低了,每个线程在想得到类的实例的时候,执行方法getInstance()方法都要进行同步,其实这个方法只需要执行一次实例化代码就够了,后面的就直接return 就可以了,方法进行同步效率太低了
package com.good.app.singleton.type1;
//懒汉式 延迟加载 获取实例的时候 在方法体中加入 同步锁 可以保证 每次获取的都是单实例 但是 效率太慢了
public class Singleton4 {
private static Singleton4 instance = null;
private Singleton4() {
}
/**
* 加了 同步锁 保证只有一个线程进来 但是 效率变得低下
*/
public static synchronized Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
5.懒汉式(线程不安全,同步代码块)
线程还是不安全,执行效率低 不推荐使用
package com.good.app.singleton.type1;
//懒汉式
public class Singleton5 {
private static Singleton5 instance = null;
private Singleton5() {
}
/**
* 在创建 实例的时候加同步锁 没有解决多线程的问题 还是会出现多个实例的情况
*/
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
instance = new Singleton5();
}
}
return instance;
}
}
6.双重检查
优点:懒加载 线程安全 推荐使用
package com.good.app.singleton.type1;
//懒汉式 双重锁机制 线程安全 也是懒加载 推荐使用
public class Singleton6 {
private static volatile Singleton6 instance = null;
private Singleton6() {
}
/**
* 创建实例之前 在加一个判断 这个时候就不会出现 多个实例被创建的情况 也是满足懒加载的
*/
public static Singleton6 getInstance() {
if (instance == null) {
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
7.静态内部类
优缺点说明:
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
- 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
- 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
- 结论:推荐使用
package com.good.app.singleton.type1;
//延迟加载 静态内部类单例模式 推荐使用
public class Singleton7 {
private Singleton7() {
}
public static class SingletonInstance{
private static final Singleton7 instance = new Singleton7();
}
/**
* 外部类装载的时候 静态内部类是不会进行装载的 保证懒加载是可以使用的
* 在调用 getInstance()的时候 获取 静态内部类中的属性 静态内部类会进行装载 jvm类进行装载的时候 线程是安全的
* 类的静态属性指挥在第一次加载类的时候初始化,
*
* @return
*/
public static Singleton7 getInstance(){
return SingletonInstance.instance;
}
}
8.枚举
优缺点说明
- 这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
- 这种方式是 Effective Java作者 JoshBloch 提倡的方式
- 结论:推荐使用
package com.good.app.singleton.type1;
//延迟加载 静态内部类单例模式 推荐使用
public enum Singleton8 {
INSTANCE;
//方法
public void method() {
System.out.println("instance method");
}
public static void main(String[] args) {
Singleton8 instance1 = Singleton8.INSTANCE;
Singleton8 instance2 = Singleton8.INSTANCE;
System.out.println("instance1==instance2 = " + (instance1 == instance2));
System.out.println("instance1.hashCode() = " + instance1.hashCode());
System.out.println("instance2.hashCode() = " + instance2.hashCode());
instance1.method();
instance2.method();
}
}
单例模式在jdk中的实现 RunTime 类 饿汉式
总结
单例模式注意事项和细节说明
-
单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
-
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
-
单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)