单例模式
- 单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
- 单例模式是创建型模式。
饿汉单例模式
- 饿汉单例模式在类的加载时候就立即初始化,并且创建对象。
- 它绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题
优点:没有加任何锁、执行效率比较高,用户体验比懒汉单例模式更好
缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能“占着茅坑不拉屎”
package org.example.spring.designpattern.singleton;
/**
* TODO 饿汉单例模式
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 08:16
*/
public class HungrySingleton {
//先静态、后动态
//先属性、后方法
//先上后下
/**
* 全局访问点,线程先没出现以前就实例化,不管用不用都占用空间
*/
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {
}
// 提供一个全局访问点
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
还有另一种写法,利用静态代码块的机制:
package org.example.spring.designpattern.singleton;
/**
* TODO 饿汉单例模式——静态代码块
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 08:19
*/
public class HungryStaticSingleton {
/**
* 提供一个全局访问点
*/
private static final HungryStaticSingleton INSTANCE ;
static {
INSTANCE = new HungryStaticSingleton();
}
private HungryStaticSingleton() {
}
public static HungryStaticSingleton getInstance() {
return INSTANCE;
}
}
饿汉单例模式适合用于单例对象比较少的情况
懒汉单例模式
懒汉单例模式,顾名思义它......比较懒,所以它的特点是:被外部类调用的时候内部类才会加载。
package org.example.spring.designpattern.singleton;
/**
* TODO 懒汉单例模式
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 08:23
*/
public class LazySimpleSingleton {
private LazySimpleSingleton() {
}
//静态块,公共内存区域
private static LazySimpleSingleton lazy = null;
/**
* 静态方法
* 实例不存在的时候new一个
* @return
*/
public static LazySimpleSingleton getInstance() {
if (lazy == null) {
lazy = new LazySimpleSingleton();
}
return lazy;
}
/**
* 创建一个线程
*/
static class testSingletonThread implements Runnable{
@Override
public void run() {
LazySimpleSingleton singleton = LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + singleton);
}
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
Thread t1 = new Thread(new testSingletonThread());
Thread t2 = new Thread(new testSingletonThread());
t1.start();
t2.start();
System.out.println("End");
}
}
运行结果如下:
以上执行结果有概率出现两种不同结果,存在很大的线程安全隐患;这里教大家一种新技能,用线程模式调试,手动控制线程的执行顺序来跟踪内存的变化。
线程模式调试
先给testSingletonThread打上断点,如下图所示:
使用鼠标右键单击断点,切换为Thread模式,如下图所示:
然后给LazySimpleton类打上断点,同样标记为Thread模式,如下图所示:
转到客户端测试代码,同样也打上断点,同时改为Thread模式,如下图所示:
开始Debug之后,会看到Debug控制台可以自由切换Thread的运行状态:
通过不断切换线程,并观测其内存状态,我们发现在线程环境下LazySimpleSingleton被实例化了两次。有时候我们得到的运行结果可能是相同的两个对象,实际上是被后面执行的线程覆盖了,我们看到了一个假象,线程安全隐患依旧存在。
那么,我们如何来优化代码,使得懒汉单例模式在线程环境下安全呢?看下面的代码,给getInstance()加上synchronized关键字,使这个方法变成线程同步方法:
双重检查锁懒汉单例模式:
package org.example.spring.designpattern.singleton;
/**
* TODO 双重检查锁懒汉单例模式
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 08:48
*/
public class LazyDoubleCheckSingleton {
//静态块,公共内存区域
private static LazyDoubleCheckSingleton lazy = null;
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance() {
if (lazy == null) {
synchronized (LazyDoubleCheckSingleton.class){
if (lazy == null) {
lazy = new LazyDoubleCheckSingleton();
// 1、分配内存给这个对象
// 2、初始化对象
// 3、设置lazy指向刚分配的内存地址
}
}
}
return lazy;
}
}
最终版本,采用静态内部类的方式:
package org.example.spring.designpattern.singleton;
/**
* TODO 采用静态内部类的懒汉单例模式
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 08:52
*/
public class LazyInnerClassSingleton {
// 使用LazyInnerClassGeneral的时候,默认会先初始化内部类
// 如果没使用,则内部类是不加载的
private LazyInnerClassSingleton() {
}
// 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个方法不会被重写、重载
public static final LazyInnerClassSingleton getInstance() {
// 在返回结果之前,一定会先加载内部类
return LazyHolder.INSTANCE;
}
// 默认不加载
private static class LazyHolder{
private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
}
}
史上最牛的单例模式实现方式【防止反射破坏掉单例】
package org.example.spring.designpattern.singleton;
/**
* TODO 自认为史上最强的单例模式
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 08:58
*/
public class LazyInnerClassSingletonPlus {
// 使用LazyInnerClassGeneral的时候,默认会先初始化内部类
// 如果没使用,则内部类是不加载的
private LazyInnerClassSingletonPlus() {
if (LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许创建多个实例");
}
}
// 每一个关键字都不是多余的,static是为了单例的空间共享,保证这个方法不会被重写、重载
public static final LazyInnerClassSingletonPlus getInstance(){
return LazyHolder.INSTANCE;
}
// 默认不加载
private static class LazyHolder{
private static final LazyInnerClassSingletonPlus INSTANCE = new LazyInnerClassSingletonPlus();
}
}
线程单例实现ThreadLocal
ThreadLocal不能保证其创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,天生是线程安全的:
package org.example.spring.designpattern.singleton;
/**
* TODO 线程单例实现ThreadLocal
*
* @author ss_419
* @version 1.0
* @date 2023/3/9 09:05
*/
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingleton = new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton() {
}
public static ThreadLocalSingleton getInstance() {
return threadLocalSingleton.get();
}
}
总结
单例模式可以保证内存里只有一个实例,减少了内存的开销,还可以避免对资源的多重占用。
标签:Spring,private,static,单例,new,线程,设计模式,public From: https://www.cnblogs.com/atwood-pan/p/17197074.html