您的位置:首页 > 编程语言 > Java开发

spring5 核心原理-学习 -单例模式

2020-04-20 15:05 435 查看

内容都是从Tom老师的书中摘抄,记录下来方便自己学习。
单例模式(singleton pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例是创建型模式。
一、饿汉式单例模式是在类加载时就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没有出现前就实例化了,不可能存在访问安全问题。饿汉式单例模式适用于单例对象较少的情况。

public class HungrySingleton {
private  static  final  HungrySingleton hungrySingleton=new HungrySingleton();
private  HungrySingleton(){}
public  static  HungrySingleton getInstance(){
return  hungrySingleton;
}
}

另一种写法

public class HungrySingleton {

private static final HungrySingleton hungrySingleton;

static {
hungrySingleton = new HungrySingleton();
}

public static HungrySingleton getInstance() {
return hungrySingleton;
}
}

二、懒汉式单例模式是指被外部类调用的时候内部类才会加载。

/*
*此种方式存在线程安全问题
*/
public class LazySimpleSingleton {
private  LazySimpleSingleton(){}
private  static LazySimpleSingleton lazySimpleSingleton=null;
public  static  LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null) {
lazySimpleSingleton=new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
public class ExectorThread implements Runnable {

public void run() {
LazySimpleSingleton simpleSingleton = LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + simpleSingleton);
}
}
public class LazySimpleSignletonTest {
public static void main(String[] args) {

Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t1.start();
t2.start();

System.out.println("end");
}
}

加入断点,右键断点,选中Thread模式

不断切换线程,观察单例对象的内存状态,发现单例对象被实例化两次,后面的实例覆盖了前面的实例,我们看到的是一个假象,认为线程是安全的

/*
*加入synchroized 关键字
*/
public class LazySimpleSingleton {
private  LazySimpleSingleton(){}
private  static LazySimpleSingleton lazySimpleSingleton=null;

public  synchronized static  LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null) {
lazySimpleSingleton=new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}

通过调试,当一个线程调用getInstance()方法时,另一个线程在调用getInstance()方法,线程的状态变为Monitor,出现阻塞,直到第一个线程执行完
兼顾线程安全又能提升程序性能的写法

/*
*
*/
public class LazySimpleSingleton {
private  LazySimpleSingleton(){}
private volatile static LazySimpleSingleton lazySimpleSingleton=null;
public  static  LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null) {
synchronized (LazySimpleSingleton.class){
if (lazySimpleSingleton == null) {
lazySimpleSingleton=new LazySimpleSingleton();
}
}
}
return lazySimpleSingleton;
}
}

通过调试,当一个线程调用getInstance()方法时,另一个线程在调用getInstance()方法,线程的状态变为Monitor,出现阻塞。此时,阻塞并不是基于整个LazySimpleSingleton类,而实在getInstance()方法内部,只要逻辑不太复杂,对于调用者而言感知不到。(此处没有测试性能差距,留个位置,测试后补充)

不使用synchronized关键字的写法

/*
*  采用静态内部类的写法,这种方式兼顾了饿汉式单例模式的内存浪费问题和synchronized的性能问题,内部类一定是在方法掉用之前初始化,巧妙避免了线程安全问题。
*/
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){}
public static  final  LazyInnerClassSingleton getInstance(){
return  LazyHolder.LAZY_INNER_CLASS_SINGLETON;
}
private static class  LazyHolder{
private  static final  LazyInnerClassSingleton LAZY_INNER_CLASS_SINGLETON=new LazyInnerClassSingleton();
}
}
/*
*  反射破坏单例  ,代码是随意写的,主要为了测试用
*/
public class LazyInnerClassSingletonTest {
public static void main(String[] args) {
Class<?> clazz=LazyInnerClassSingleton.class;
Constructor<?> constructor= null;
try {
constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
try {
Object o1=constructor.newInstance();
Object o2=constructor.newInstance();
System.out.println(o1==o2);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
/*
*  序列化破坏单例
*/
public class Seriablesingleton implements Serializable {
private   final  static Seriablesingleton INSTANCE=new Seriablesingleton();
private  Seriablesingleton(){}
public  static Seriablesingleton getInstance(){
return  INSTANCE;
}
//    private Object readResolve(){
//        return  INSTANCE;
//    }
}
/*
*  将上述代码注释部分取消注释即可
* 虽然增加readResolve()方法返回实例解决了单例模式被破坏问题,但实际上实例化了两次,只是新创建的实例没有被返回。
* 如果创建对象的动作发生频率加快,就意味着内存分配开销也会随之增大
*/
public class SeriablesingletonTest {
public static void main(String[] args) {
Seriablesingleton s1=null;
Seriablesingleton s2=Seriablesingleton.getInstance();
FileOutputStream fos=null;
try {
fos=new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.close();
FileInputStream fis=new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois=new ObjectInputStream(fis);
s1= (Seriablesingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);

} catch (Exception e) {
e.printStackTrace();
}
}
}
  • 点赞 1
  • 收藏
  • 分享
  • 文章举报
guoge1982 发布了10 篇原创文章 · 获赞 1 · 访问量 219 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: