标签:存储 err 懒汉式 java style get ice span oid
1.
解决问题:一个全局使用的对象要频繁的创建和销毁
实现单例模式的要素:①类只有一个实例 ②由类自行创建这个实例 ③提供一个获取这个实例的方法
实现:①类提供静态成员存储此单例对象 ②构造私有 ③提供get方法获取单例对象
1.1 饿汉式
①静态变量--类加载时就初始化了这个变量
package com.sjms; public class Singleton { private static final Singleton simple = new Singleton(); private Singleton(){} public static Singleton getInstance() { return simple; } }
测试:
ExecutorService es = Executors.newCachedThreadPool(); for(int i=0;i<30;i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { Singleton s = Singleton.getInstance(); System.out.println(s); } }); es.execute(thread); } es.shutdown();
②静态代码块初始化
package com.sjms; public class Singletonv2 { private static Singletonv2 simple;
private Singletonv2(){} static { simple = new Singletonv2(); } public static Singletonv2 getInstance() { return simple; } }
测试:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestSJMS { public static void main(String[] args) { ExecutorService es = Executors.newCachedThreadPool(); for(int i=0;i<30;i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { Singletonv2 s = Singletonv2.getInstance(); System.out.println(s); } }); es.execute(thread); } es.shutdown(); } }
静态代码块只在类初次加载时调用,因此只会创建一份
多个线程反射调用试试看:
package com.sjms; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestSJMS { public static void main(String[] args) { ExecutorService es = Executors.newCachedThreadPool(); for(int i=0;i<30;i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { try { Class aClass = Class.forName("com.sjms.Singletonv2"); Method getInstance = aClass.getMethod("getInstance"); System.out.println(getInstance.invoke(null)); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } // Singletonv2 s = Singletonv2.getInstance(); // System.out.println(s); } }); es.execute(thread); } es.shutdown(); } }
饿汉式的问题:类加载就创建单例对象,如果这个对象一直没有用到,此单例对象还是会创建
1.2 懒汉式
类加载时不创建,获取时才创建---延迟创建
多线程不安全:会创建多个对象,不是单例了
package com.sjms; public class Singleton2 { private static Singleton2 simple; private Singleton2(){} public static Singleton2 getInstance() { if(simple == null) { simple = new Singleton2(); } return simple; } }
测试:
package com.sjms; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestSJMS { public static void main(String[] args) { ExecutorService es = Executors.newCachedThreadPool(); for(int i=0;i<30;i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { Singleton2 s = Singleton2.getInstance(); System.out.println(s); } }); es.execute(thread); } es.shutdown(); } }
测试结果:创建了多个对象
(2)懒汉式-->对获取方法加锁
上面方法的问题:假设两个线程,线程A刚判断simple为空间,准备去创建对象,此时simple还是为null;线程B也判断simple为null,于是也去创建了一个对象
解决:对获取方法加锁---解决了问题,但效率也低了很多
package com.sjms; /*懒汉式:对获取方法加锁*/ public class Simpletom2v2 { private static Simpletom2v2 simple; private Simpletom2v2(){} public synchronized static Simpletom2v2 getInstance(){ if(simple == null) { simple = new Simpletom2v2(); } return simple; } }
(3)懒汉式-->双检锁
上面对整个方法加锁,效率较低
package com.sjms; public class Simpleton2v3 { private static Simpleton2v3 simple; private Simpleton2v3(){} public static Simpleton2v3 getInstance() { if(simple == null) { synchronized (Simpleton2v3.class) { if(simple == null) { simple = new Simpleton2v3(); } } } return simple; } }
问题:由于JVM的指令重排序,线程A先进入锁住的代码块创建对象,但还未创建完成;线程B进入第一个判断,simple不为空,于是直接返回simple,如果线程B接着立刻对这个单例对象进行使用,会造成无法预知的错误。
这里的指令重排序:线程A:①分配内存空间 ②在这块内存上创建Simpleton2v3实例 ③将simple引用指向这块内存
本来的步骤应该是这样,但由于②、③没有依赖关系,可能会使③先于②执行,造成线程B使用了不完整的实例对象
问题即:创建对象不是原子操作
问题解决:volatile 禁止指令重排序,即对 simple 加 volatile 修饰
package com.sjms; /*懒汉式:双检锁*/ public class Simpleton2v3 { // private static Simpleton2v3 simple; private volatile static Simpleton2v3 simple; private Simpleton2v3(){} public static Simpleton2v3 getInstance() { if(simple == null) { synchronized (Simpleton2v3.class) { if(simple == null) { simple = new Simpleton2v3(); } } } return simple; } }
1.3 静态内部类实现
综合饿汉式的 静态成员(不用锁) 以及 懒汉式的获取时才创建
效率高、线程安全、可延迟加载
package com.sjms; /*静态内部类实现:线程安全、效率高、可延迟加载*/ public class Simpleton3 { private Simpleton3(){} private static class SimpletonClass{ private static Simpleton3 instance = new Simpleton3(); } public static Simpleton3 getInstance() { return SimpletonClass.instance; } }
1.4 枚举
public enum Simpleton4 { INSTANCEA; }
enum Simpleton5 { INSTANCEA, INSTANCEB }
枚举的使用:
标签:存储 err 懒汉式 java style get ice span oid
原文地址:https://www.cnblogs.com/taoXiang/p/13170230.html