饿汉式、DCL懒汉式、深究!
饿汉式:
//饿汉式单例
//可能会浪费空间
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式:
//懒汉式单例
public class LazyMan {
private LazyMan(){
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
注意:懒汉式单例在单线程模式下,单例模式是没有问题的。但是在多线程并发的情况下,单例模式偶尔成功,偶尔失败。为了解决这个问题,需要加锁处理。如下代码所示:
//懒汉式单例
public class LazyMan {
private LazyMan(){
}
private static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例。 简称DCL懒汉式
//主要目的是解决多线程并发单例失败问题。
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
注意:这个时候还不是完美的,有两个原因,不是原子性操作,,导致的指令重排现象,分析如下:
//懒汉式单例
public class LazyMan {
private LazyMan(){
}
private static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例。 简称DCL懒汉式
//主要目的是解决多线程并发单例失败问题。
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();//不是一个原子性操作
// 1.分配内存空间
// 2.执行构造方法,初始化对象
// 3.把这个对象指向这个空间
//
// 期望的顺序是:123
// 但是会有:132 A 的情况
// 如果再来一个B,此时lazyMan还没有完成构造
}
}
}
return lazyMan;
}
}
为了解决这个问题,需要加入volatile,变成原子性操作,改变代码如下:
// private static LazyMan lazyMan;
private volatile static LazyMan lazyMan;
静态内部类:
//静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
注意:以上提到的这些单例,并不安全。反射会破坏这种单例模式。
对DCL懒汉模式增加反射,破解单例,代码如下:
//懒汉式单例
public class LazyMan {
private LazyMan(){
}
private volatile static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例。 简称DCL懒汉式
//主要目的是解决多线程并发单例失败问题。
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//反射!
public static void main(String[] args) throws Exception{
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
运行结果:
根据结果:如果是单例模式的话,说明两种结果是一致的。但是,这里结果不一致,说明反射会破坏单例模式,那对于这种反射,应该怎么去解决呢?在构造方法中增加判断,代码如下:
//懒汉式单例
public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){
if(lazyMan != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例。 简称DCL懒汉式
//主要目的是解决多线程并发单例失败问题。
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//反射!
public static void main(String[] args) throws Exception{
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
运行结果:
虽然加了一个判断,让反射无法破坏单例,但是对反射处进行改进,仍然可以对单例进行破坏,改进的代码如下所示:
//懒汉式单例
public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){
if(lazyMan != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例。 简称DCL懒汉式
//主要目的是解决多线程并发单例失败问题。
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//反射!
public static void main(String[] args) throws Exception{
//LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
结果如图所示:
针对这种情况,仍然有方法可以防止该反射的破坏,增加红绿灯。代码如下:
//懒汉式单例
public class LazyMan {
private static boolean wuyinliang = false;
private LazyMan(){
synchronized (LazyMan.class){
if(wuyinliang == false){
wuyinliang = true;
}else{
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例。 简称DCL懒汉式
//主要目的是解决多线程并发单例失败问题。
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//反射!
public static void main(String[] args) throws Exception{
//LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
结果如图所示:
枚举类型:
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举类型不细讲了,也能被破译,不过安全性更高