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
我们能够发现,我们创建了两个不同的实例,这种情况下,我们应该怎样解决呢,答案很简单。
再次运行程序: