饿汉式单例
package com.hua.DesignPatterns.single;
/*
* 饿汉式单例,一上来就创建对象,浪费内存
*
* */
public class Hungry {
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private byte[] data3=new byte[1024*1024];
private byte[] data4=new byte[1024*1024];
private Hungry(){
}
private static final Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式单例
package com.hua.DesignPatterns.single;
public class LazyMan1 {
private LazyMan1(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static LazyMan1 LAZYMAN1=null;
public static LazyMan1 getInstance(){
/**
* 因为getInstance方法没有加锁,多个线程能同时进入这个方法
* 导致多个线程能同时进入到LAZYMAN1=new LazyMan1();
* 所以单例模式会被破坏
*/
if (LAZYMAN1==null){
LAZYMAN1=new LazyMan1();
return LAZYMAN1;
}
return LAZYMAN1;
}
public static void main(String[] args) {
for (int i=0;i<10;i++){
new Thread(()->{
LazyMan1 lazyMan1=LazyMan1.getInstance();
}).start();
}
}
}
运行结果
解决办法:双重检测锁模式
package com.hua.DesignPatterns.single;
public class LazyMan1 {
private LazyMan1(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static LazyMan1 LAZYMAN1=null;
public static LazyMan1 getInstance(){
/**
* 双重检测锁模式 首先判断LAZYMAN1是否为空,为空的话,加一把类锁
* 然后在同步代码块里面再次判断判断LAZYMAN1是否为空,
* 如果为空的话就new一个对象
*/
if (LAZYMAN1==null){
synchronized (LazyMan1.class){
if (LAZYMAN1==null){
LAZYMAN1=new LazyMan1();
}
}
}
return LAZYMAN1;
}
public static void main(String[] args) {
for (int i=0;i<10;i++){
new Thread(()->{
LazyMan1 lazyMan1=LazyMan1.getInstance();
}).start();
}
}
}
双重检测锁模式下可能出现的问题
package com.hua.DesignPatterns.single;
public class LazyMan1 {
private LazyMan1(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static volatile LazyMan1 LAZYMAN1=null;
public static LazyMan1 getInstance(){
if (LAZYMAN1==null){
synchronized (LazyMan1.class){
if (LAZYMAN1==null){
/*
* 不是原子性操作,分三步
* 1.分配内存空间
* 2.使用构造函数初始化对象
* 3.把 LAZYMAN1指向初始化后的空间
* 正常的执行顺序是 123
* 由于指令重排可能导致 132的执行顺序,当执行到13的时候 另一个线程进入到这个函数
* 判断LAZYMAN1 不等于空,这时就直接返回LAZYMAN1,然而LAZYMAN1还没有初始化
* 这时候就会产生问题
* 解决方式 在LAZYMAN1 前加 volatile 修饰
* */
LAZYMAN1=new LazyMan1();
}
}
}
return LAZYMAN1;
}
public static void main(String[] args) {
for (int i=0;i<10;i++){
new Thread(()->{
LazyMan1 lazyMan1=LazyMan1.getInstance();
}).start();
}
}
}
静态内部类实现
package com.hua.DesignPatterns.single;
/*静态内部类实现*/
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
public static final Holder HOLDER=new Holder();
}
}
反射可以破坏单例
解决办法:在构造函数中设置一个同步代码块,然后在类中设置一个标志位,如果第一次进入构造函数就正确通过,其他的就抛出一个异常
package com.hua.DesignPatterns.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyMan1 {
public static boolean just=false;
private LazyMan1(){
synchronized (LazyMan1.class){
if (just==false){
just=true;
System.out.println("生成单例对象");
}else{
throw new RuntimeException("不要试图用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName()+"ok");
}
private static volatile LazyMan1 LAZYMAN1=null;
public static LazyMan1 getInstance(){
if (LAZYMAN1==null){
synchronized (LazyMan1.class){
if (LAZYMAN1==null){
/*
* 不是原子性操作,分三步
* 1.分配内存空间
* 2.使用构造函数初始化对象
* 3.把 LAZYMAN1指向初始化后的空间
* 正常的执行顺序是 123
* 由于指令重排可能导致 132的执行顺序,当执行到13的时候 另一个线程进入到这个函数
* 判断LAZYMAN1 不等于空,这时就直接返回LAZYMAN1,然而LAZYMAN1还没有初始化
* 这时候就会产生问题
* 解决方式 在LAZYMAN1 前加 volatile 修饰
* */
LAZYMAN1=new LazyMan1();
}
}
}
return LAZYMAN1;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<LazyMan1> declaredConstructor = LazyMan1.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan1 man1 = declaredConstructor.newInstance();
LazyMan1 man2 = declaredConstructor.newInstance();
System.out.println(man1==man2);
}
}
运行结果
枚举:
package com.hua.DesignPatterns.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//enum
public enum EnumSingle {
INSTANCE;
public static EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle single1=EnumSingle.getInstance();
EnumSingle single2=EnumSingle.getInstance();
System.out.println(single1==single2);
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumSingle single3 = constructor.newInstance();
System.out.println(single1==single3);
}
}
运行结果: