目的:保证实例全局唯一
构造器私有!!!
一上来就加载!可能会浪费内存
package com.tiko.single; public class Hungry {
private byte[] data1 = new byte[1024]; private byte[] data2 = new byte[1024]; private Hungry(){
} private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance(){
return HUNGRY; } }
package com.tiko.single; public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()); } private static LazyMan LAZYMAN; public static LazyMan getInstance(){
if (LAZYMAN == null){
//没有的时候才创建 LAZYMAN = new LazyMan(); } return LAZYMAN; } //以上的代码,在单线程下是没有问题的,但是在多线程下是有问题,以下是测试 public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan.getInstance(); }).start(); } } }
package com.tiko.single; public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()); } private static volatile LazyMan LAZYMAN; //双重检测锁模式,懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){
if (LAZYMAN==null){
synchronized (LazyMan.class){
//这里的同步锁 类名.class是什么意思? if (LAZYMAN == null){
LAZYMAN = new LazyMan();//不是原子性操作 /** * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个操作 * 在这些操作中,存在指令重排,我们希望是123,可能执行的时候是132 * 所以为了避免指令重排,需要在对象LAZYMAN加上volatile关键字,来避免指令重排 * */ } } } return LAZYMAN; } //以上的代码,在单线程下是没有问题的,但是在多线程下是有问题,以下是测试 public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan.getInstance(); }).start(); } } }
package com.tiko.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName()); } private static volatile LazyMan LAZYMAN; //双重检测锁模式,懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){
if (LAZYMAN==null){
synchronized (LazyMan.class){
//这里的同步锁 类名.class是什么意思? if (LAZYMAN == null){
LAZYMAN = new LazyMan();//不是原子性操作 /** * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个操作 * 在这些操作中,存在指令重排,我们希望是123,可能执行的时候是132 * 所以为了避免指令重排,需要在对象LAZYMAN加上volatile关键字,来避免指令重排 * */ } } } return LAZYMAN; } //以上的代码,在单线程下是没有问题的,但是在多线程下是有问题,以下是测试 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } } 程序输出: main main com.tiko.single.LazyMan@1540e19d com.tiko.single.LazyMan@b6 很明显,结果不符合单例的要求!
package com.tiko.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){
if(LAZYMAN!=null){
throw new RuntimeException("不要通过反射来破坏!"); } } System.out.println(Thread.currentThread().getName()); } private static volatile LazyMan LAZYMAN; //双重检测锁模式,懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){
if (LAZYMAN==null){
synchronized (LazyMan.class){
//这里的同步锁 类名.class是什么意思? if (LAZYMAN == null){
LAZYMAN = new LazyMan();//不是原子性操作 /** * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个操作 * 在这些操作中,存在指令重排,我们希望是123,可能执行的时候是132 * 所以为了避免指令重排,需要在对象LAZYMAN加上volatile关键字,来避免指令重排 * */ } } } return LAZYMAN; } //以上的代码,在单线程下是没有问题的,但是在多线程下是有问题,以下是测试 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } } 结果输出: Exception in thread "main" java.lang.reflect.InvocationTargetException ... at com.tiko.single.LazyMan.main(LazyMan.java:47) Caused by: java.lang.RuntimeException: 不要通过反射来破坏!
package com.tiko.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class LazyMan {
private LazyMan(){
synchronized (LazyMan.class){
if(LAZYMAN!=null){
throw new RuntimeException("不要通过反射来破坏!"); } } System.out.println(Thread.currentThread().getName()); } private static volatile LazyMan LAZYMAN; //双重检测锁模式,懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){
if (LAZYMAN==null){
synchronized (LazyMan.class){
//这里的同步锁 类名.class是什么意思? if (LAZYMAN == null){
LAZYMAN = new LazyMan();//不是原子性操作 /** * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个操作 * 在这些操作中,存在指令重排,我们希望是123,可能执行的时候是132 * 所以为了避免指令重排,需要在对象LAZYMAN加上volatile关键字,来避免指令重排 * */ } } } return LAZYMAN; } //以上的代码,在单线程下是没有问题的,但是在多线程下是有问题,以下是测试 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance1 = declaredConstructor.newInstance(); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } } 程序输出: main main com.tiko.single.LazyMan@1540e19d com.tiko.single.LazyMan@b6 违背单例
package com.tiko.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class LazyMan {
private static boolean unknownFlag = false; private LazyMan(){
synchronized (LazyMan.class){
if (unknownFlag == false){
unknownFlag = true; }else {
throw new RuntimeException("不要通过反射来破坏!"); } } System.out.println(Thread.currentThread().getName()); } private static volatile LazyMan LAZYMAN; //双重检测锁模式,懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){
if (LAZYMAN==null){
synchronized (LazyMan.class){
//这里的同步锁 类名.class是什么意思? if (LAZYMAN == null){
LAZYMAN = new LazyMan();//不是原子性操作 /** * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个操作 * 在这些操作中,存在指令重排,我们希望是123,可能执行的时候是132 * 所以为了避免指令重排,需要在对象LAZYMAN加上volatile关键字,来避免指令重排 * */ } } } return LAZYMAN; } //以上的代码,在单线程下是没有问题的,但是在多线程下是有问题,以下是测试 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance1 = declaredConstructor.newInstance(); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } } 结果输出: at com.tiko.single.LazyMan.main(LazyMan.java:51) Caused by: java.lang.RuntimeException: 不要通过反射来破坏!
package com.tiko.single; public enum EnumSingle {
INSTANCE; public static EnumSingle getInstance() {
return INSTANCE; } } //测试一下 class Test{
public static void main(String[] args) {
EnumSingle instance = EnumSingle.INSTANCE; EnumSingle instance1 = EnumSingle.getInstance(); EnumSingle instance2 = EnumSingle.getInstance(); System.out.println(instance); System.out.println(instance1); System.out.println(instance2); } } 结果输出: INSTANCE INSTANCE INSTANCE 符合单例要求
package com.tiko.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public enum EnumSingle {
INSTANCE; public static 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(null); declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } } 结果输出: Exception in thread "main" java.lang.NoSuchMethodException:
反编译,cmd->javap -p EnumSingle.class
jad工具:cmd->jad -sjava EnumSingle.class
发现枚举类的构造函数如下:
源码真得骗人了!!!
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
package com.tiko.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public enum EnumSingle {
INSTANCE; public static 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); } } 结果输出: Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects 说明反射确实无法破坏枚举的单例
参考自狂神说