单例模式的七种实现-Singleton(Java实现)
2018-06-18 01:28:37来源:未知 阅读 ()
饿汉式
实现代码:
public class Singleton { private Singleton() { } private static Singleton singleton = new Singleton(); public static Singleton getInstance() { return singleton; } }
验证一下:
public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true }
如果用反射, 是否仍然是单例:
结果是反射破坏了单例
public static void main(String[] args) throws Exception { // 自定义单例方法获取 Singleton s1 = Singleton.getInstance(); // 反射获取 Constructor constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton s2 = (Singleton) constructor.newInstance(); System.out.println(s1 == s2); //false }
懒汉式
将上面的饿汉式改为懒汉式:
public class Singleton { private Singleton() { } private static Singleton singleton; public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
验证一下:
public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true }
不过这是一种线程不安全的单例实现.
我们在Singleton中加上sleep来模拟一下线程切换:
public class Singleton { private Singleton() { } private static Singleton singleton; public static Singleton getInstance() { if (singleton == null) { try { Thread.sleep(0); } catch (InterruptedException e) { e.printStackTrace(); } singleton = new Singleton(); } return singleton; } }
验证一下线程不安全:
public class Main3 { private static LinkedBlockingQueue<Singleton> singletons = new LinkedBlockingQueue<>(); public static void main(String[] args) throws Exception{ ExecutorService threadPool = Executors.newFixedThreadPool(10); for(int i= 0;i<100;i++){ threadPool.execute(()->{ singletons.offer(Singleton.getInstance()); }); } Singleton basic = singletons.take(); while(basic==singletons.take()){ System.out.println("continue"); continue; } System.out.println("走到这里说明单例失败"); } }
懒汉式+同步方法
public class Singleton { private Singleton() { } private static Singleton singleton; public synchronized static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
验证一下:
public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true }
由于是同步, 所以同步方法内不会出现多线程执行的情况.
懒汉式+双重校验锁
因为上面那种会每次进时都会进行同步锁, 很浪费性能, 所以在加锁之间先进行校验
public class Singleton{ private Singleton() { } private static Singleton singleton; public static Singleton getInstance() { if (singleton==null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
验证一下性能:
能明显看出来性能差距...5千倍...
同步方法, 即直接在方法声明处加了Synchronize的情况:
public static void main(String[] args){ long start = System.currentTimeMillis(); for(int i= 0;i<999999999;i++){ Singleton s = Singleton.getInstance(); } System.out.println(System.currentTimeMillis()-start); }
双重校验锁:
public static void main(String[] args){ long start = System.currentTimeMillis(); for(int i= 0;i<999999999;i++){ Singleton s = Singleton.getInstance(); } System.out.println(System.currentTimeMillis()-start); }
懒汉式+双重校验锁+防止指令重拍
看似简单的一段赋值语句:instance = new Singleton(); 其实JVM内部已经转换为多条指令:
memory = allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
但是经过重排序后如下:
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化
ctorInstance(memory); //2:初始化对象
可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。
加上volatile关键字:
public class Singleton { private Singleton() { } private volatile static Singleton singleton; public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
验证一下性能:
能明显看出来加了volatile后对性能的影响, 由之前的5, 变为了302...
性能下降了, 但是相比于上面的双重校验锁, 更保证了线程安全.
public static void main(String[] args){ long start = System.currentTimeMillis(); for(int i= 0;i<999999999;i++){ Singleton s = Singleton.getInstance(); } System.out.println(System.currentTimeMillis()-start); }
静态内部类
这也是一种很好的实现方式, 不仅懒加载, 还保证了线程安全, 性能也很好, 实现起来也很简单
public class Singleton { private static class LazyHolder { private static final Singleton instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return LazyHolder.instance; } }
验证一下性能:
public static void main(String[] args){ long start = System.currentTimeMillis(); for(int i= 0;i<999999999;i++){ Singleton s = Singleton.getInstance(); } System.out.println(System.currentTimeMillis()-start); }
枚举
个人对枚举类型的理解还有限, 有待学习....
public enum Singleton { INSTANCE; private String name; Singleton() { this.name = "king"; } public static Singleton getInstance() { return INSTANCE; } public String getName() { return this.name; } }
验证一下性能:
public static void main(String[] args){ long start = System.currentTimeMillis(); for(int i= 0;i<999999999;i++){ Singleton s = Singleton.getInstance(); } System.out.println(System.currentTimeMillis()-start); }
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:缓存与数据库的一致性
下一篇:Java学习过程中的收获
- 设计模式-委派/策略模式 2020-06-09
- 深入理解:设计模式中的七大设计原则 2020-06-07
- 设计模式---类之间的关系知多少 2020-06-07
- 你与面试官所了解的单例模式并不一样! 2020-06-06
- 高手眼中的观察者模式有什么不一样 2020-06-05
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash