Java设计模式之--单例模式-使用懒汉模式创建

一、这是在单线程的情况下创建的
package com.sinosoft.test;

import javax.jnlp.SingleInstanceListener;

/**
*
*/
public class SingletonTest {
public static void main(String[] args) {
//可见这种方法就创建不了了
// LazySingleton lazySingleton=new LazySingleton();
LazySingleton lazySingleton = LazySingleton.getInstance();
}


}

//使用的时候才开始初始化,jvm运行的整个期间都只有一个实例
class LazySingleton {
private static LazySingleton instance;

public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}

//将new 对象的构造方法屏蔽掉,不允许在外部进行多次创建
private LazySingleton() {

}

}

二、多线程场景下
package com.sinosoft.test;

import javax.jnlp.SingleInstanceListener;
import java.sql.Time;
import java.util.concurrent.TimeUnit;

/**
*
*/
public class SingletonTest {
public static void main(String[] args) {
//可见这种方法就创建不了了
// LazySingleton lazySingleton=new LazySingleton();


//下面我们验证一下在多线程的情况下看看会不会创建多个实例
new Thread(new Runnable() {
@Override
public void run() {
LazySingleton lazySingleton = LazySingleton.getInstance();
//通过打印他的内存地址来进行验证
System.out.println("============" + lazySingleton);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
LazySingleton lazySingleton = LazySingleton.getInstance();
//通过打印他的内存地址来进行验证
System.out.println("============" + lazySingleton);
}
}).start();
}


}

//使用的时候才开始初始化,jvm运行的整个期间都只有一个实例
class LazySingleton {
private static LazySingleton instance;

public static LazySingleton getInstance() {
try {
if (instance == null) {
// 主要是模拟高并发场景下线程之间的调度
TimeUnit.MILLISECONDS.sleep(1200);
instance = new LazySingleton();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}

return instance;
}

//将new 对象的构造方法屏蔽掉,不允许在外部进行多次创建
private LazySingleton() {

}

}

运行结果:
我们可以看到创建了两个实例:

============com.sinosoft.test.LazySingleton@606232e4
============com.sinosoft.test.LazySingleton@7c60d4cb

出现这种情况的原因是:在同一时刻有两个线程在同时访问这个方法。怎样避免,我们可以将synchronized关键字,让同一时刻只能有一个线程进行访问。

//使用的时候才开始初始化,jvm运行的整个期间都只有一个实例
class LazySingleton {
private static LazySingleton instance;

public static synchronized LazySingleton getInstance() {
try {
if (instance == null) {
// 主要是模拟高并发场景下线程之间的调度
TimeUnit.MILLISECONDS.sleep(1200);
instance = new LazySingleton();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}

return instance;
}

//将new 对象的构造方法屏蔽掉,不允许在外部进行多次创建
private LazySingleton() {

}

运行结果:

============com.sinosoft.test.LazySingleton@606232e4
============com.sinosoft.test.LazySingleton@606232e4

其实上面这种加锁的方式是比较重的一种方法,固定死了一次就只能有一个线程进行访问,不利于高并发场景。

解决这种问题,我们可以使用双重校验(或者叫双重锁)

//使用的时候才开始初始化,jvm运行的整个期间都只有一个实例
class LazySingleton {
private static LazySingleton instance;

public static LazySingleton getInstance() {
try {
if (instance == null) {
// 主要是模拟高并发场景下线程之间的调度
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}

return instance;
}

//将new 对象的构造方法屏蔽掉,不允许在外部进行多次创建
private LazySingleton() {

}

运行结果:

============com.sinosoft.test.LazySingleton@52a17671
============com.sinosoft.test.LazySingleton@52a17671

当然为了防止jvm指令重排序,我们加上volatile关键字进行修饰:

private static volatile LazySingleton instance;

三、使用静态内部类进行单例模式的创建,这也是懒汉模式的一种实现方式
package com.sinosoft.test;

/**
* 这是通过静态内的方式实现单例模式,也是懒加载的一种实现方式
*/
public class InnerClassSingleton {
static class InnerClass{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
//这是提供给外部进行访问和创建单例的方法
public static InnerClassSingleton getInstance(){
return InnerClass.instance;
}

private InnerClassSingleton(){

}
}

接下来,我们通过反射机制来破坏单例类的创建
package com.sinosoft.test;

import java.lang.reflect.Constructor;

/**
* 接下来,我们对我们创建的单例类通过反射的机制进行破坏,也就是说创建多个实例
*/
public class Anti {
public static void main(String[] args) {
try{
//使用反射的机制获取实例
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();

//使用静态内部类的方式获取实例
InnerClassSingleton instance = InnerClassSingleton.getInstance();

System.out.println(innerClassSingleton==instance);
}catch (Exception e){
e.printStackTrace();
}

}
}
运行结果:

false

我们能够发现,我们创建了两个不同的实例,这种情况下,我们应该怎样解决呢,答案很简单。

 再次运行程序:

原文地址:https://www.cnblogs.com/dongyaotou/p/13568771.html