九大常用设计模式学习-单例模式

版权声明:本文为博主原创文章,如果帮到你请点赞☺☺☺。 https://blog.csdn.net/qq_37902949/article/details/87903569

设计模式学习记录:一句话+代码体会设计模式。

 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局[static]访问点。

四种实现方式 

方式一:

/**
 * <pre>
 * 饿汉式: 如果这个类初始化过程中不是非常消耗性能可以采用该方式, 天生线程安全
 * <pre>
 */
public class BeanXMLReader {
	
	private static BeanXMLReader reader = new BeanXMLReader();
	
	private BeanXMLReader() {
		System.out.println("------instance BeanXMLReader-----");
	}
	
	public static BeanXMLReader newInstance() {
		return reader;
	}
}

方式二:

/**
 * <pre>
 * 懒汉式: 需要的时候再实例,非线程安全,所以要加同步块锁保证线程安全,但效率较一般
 * <pre>
 */
public class BeanXMLReader1 {

	// 防止指令重排添加 volatile
	private volatile static BeanXMLReader1 reader;
	
	private BeanXMLReader1() {
		System.out.println("------instance BeanXMLReader-----");
	}

	// 线程安全方式1,效率不好:
	// 同步锁:synchronized
	/*public synchronized static BeanXMLReader1 newInstance() {
		if (reader == null) {
			reader = new BeanXMLReader1(); //就是放到这里实例
		}
		return reader;
	}*/
	
	// 线程安全方式2:
	// 需要volatile防止指令重排。
	// 初始化步骤(可能不完全构造情况:2和3步骤倒换):
	// 1、allocate 内存空间 => reader
	///2、reader = new BeanXMLReader1(); 构造类
	///3、construtor 构造函数初始化对象。
	// synchronized 添加到方法上效率低,添加到方法块内好些
	public static BeanXMLReader1 newInstance() {
		if (reader == null) {
			//同步块
			synchronized (BeanXMLReader1.class) {
				if (reader == null) {
					reader = new BeanXMLReader1();
				}
			}
		}
		return reader;
	}
	
}

方式三:

/**
 * 枚举实现单例方式,枚举天生单例,jdk1.5后推荐写法,效率高
 * jdk保证线程安全
 */
public enum SingletonEnum {
	/**
	 * 枚举常量
	 * 可以不用括号:INSTANCE();
	 * 调用构造函数
	 */
	INSTANCE;
	
	private BeanXMLReader reader;

	private SingletonEnum() {
		System.out.println("-------------------");
		reader = new BeanXMLReader();
	}
	
	public BeanXMLReader getReader() {
		return reader;
	}

	//内部类
	public class BeanXMLReader {
		public void sayHello() {
			System.out.println("调用了枚举类中提供方法");
		}
	}
}

方式四:

/**
 * <pre>
 * 使用内部类实现单例
 * 类的线程安全是虚拟机保证的
 * <pre>
 */
public class BeanXMLReader2 {
	
	public static BeanXMLReader2 getInstance() {
		return InnerClass.reader;
	}

	// 类的线程安全是虚拟机保证的,静态内部类初始化对象
	private static class InnerClass {
		private static BeanXMLReader2 reader = new BeanXMLReader2();
	}
}

测试:

    @Test
    public void testSingleton1() throws Exception {
        System.out.println(BeanXMLReader.newInstance());
        System.out.println(BeanXMLReader.newInstance());
        System.out.println(BeanXMLReader.newInstance());
        System.out.println(BeanXMLReader.newInstance());
        System.out.println(BeanXMLReader.newInstance());
        System.out.println(BeanXMLReader.newInstance());
        System.out.println(BeanXMLReader.newInstance());
        System.out.println("------------------------");
        ////////////////////////// 模拟线程并发 /////////////////////
        // 测试是否是线程安全的
        Set<Object> sets = Collections.synchronizedSet(new HashSet<>());
        int count = 1000;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0 ;  i< count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    sets.add(BeanXMLReader.newInstance());
                    latch.countDown();
                }
            }).start();
        }
        latch.await(); // 当latch.countDown() = 0
        Thread.currentThread().join(5);
        System.out.println(sets.size());
    }

    @Test
    public void testSingleton2() throws Exception {
        ////////////////////////// 模拟线程并发 /////////////////////
        // 测试是否是线程安全的
        // Collections.synchronizedSet:Set去重
        Set<Object> sets = Collections.synchronizedSet(new HashSet<>());
        int count = 10000;
        //发令枪,阻塞线程,让线程并发
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0 ;  i< count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    sets.add(BeanXMLReader2.getInstance());
                    latch.countDown(); //递减
                }
            }).start();
        }
        latch.await(); // 阻塞,当latch.countDown()=0放开
        Thread.currentThread().join(5); //@Test测试只是main的子线程,添加到主线程
        // 如果sets.size()=1就表示是线程安全的,否则是线程不安全的,多测试几次
        System.out.println("sets.size():"+sets.size());
    }

    @Test // 暴力反射创建单例类对象
    public void testSingleton3ForRflect() throws Exception {
        BeanXMLReader1 instance = BeanXMLReader1.newInstance();
        BeanXMLReader1 instance1 = BeanXMLReader1.newInstance();
        BeanXMLReader1 instance2 = BeanXMLReader1.newInstance();
        System.out.println(instance+","+instance1+","+instance2);
        System.out.println("------暴力反射创建单例类对象-------");
        Class<?> clazz = BeanXMLReader1.class;
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        instance = (BeanXMLReader1) constructor.newInstance();
        System.out.println(instance);
        //打印结果不一样,单例被破坏
    }

    @Test
    public void testEnum() throws Exception {
        System.out.println(SingletonEnum.INSTANCE.getReader());
        System.out.println(SingletonEnum.INSTANCE.getReader());
        System.out.println(SingletonEnum.INSTANCE.getReader());
        System.out.println(SingletonEnum.INSTANCE.getReader());
        System.out.println(SingletonEnum.INSTANCE.getReader());
        SingletonEnum.INSTANCE.getReader().sayHello();
        System.out.println("-------------尝试暴力破解-----------------");
        Class<?> cl = SingletonEnum.class;
        Class<?>[] classes = cl.getClasses();
        System.out.println(Arrays.toString(classes));
        // 获取枚举中定义单例类
        Class<?> clazz = classes[0];
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object instance = constructor.newInstance();
        System.out.println(instance);
        //运行报错:java.lang.NoSuchMethodException,表示无法暴力破解
    }

    @Test // 使用内部类实现
    public void testSingleton3() throws Exception {
        System.out.println(BeanXMLReader2.getInstance());
        System.out.println(BeanXMLReader2.getInstance());
        System.out.println(BeanXMLReader2.getInstance());
        System.out.println(BeanXMLReader2.getInstance());
        System.out.println(BeanXMLReader2.getInstance());
        System.out.println(BeanXMLReader2.getInstance());
    }

设计模式是软件开发人员在软件开发过程中面临一般问题的解决方案。

设计模式还是要慢慢体会。。

猜你喜欢

转载自blog.csdn.net/qq_37902949/article/details/87903569