- 浏览: 484154 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (301)
- Swing技术 (1)
- Linux (1)
- Javascript (22)
- 数据结构和算法 (3)
- J2SE (36)
- workflow (5)
- 设计模式 (14)
- web service (19)
- Ajax (14)
- 中间件 & 服务器 (8)
- 多线程 (9)
- Oracle (52)
- sys & soft (10)
- JMS (3)
- sso (9)
- android (11)
- struts2 (10)
- web协议 (2)
- 分布式 (2)
- PM (2)
- OLAP (3)
- Redis (2)
- Hibernate (7)
- ibatis (2)
- SQLServer (1)
- maven (3)
- Spring (7)
- Jsp (2)
- slf4j (1)
- jQuery (15)
- 权限 (1)
- 系统集成 (1)
- 笔记 (1)
- Freemarker (2)
- 项目管理 (1)
- eclipse (3)
- GIS (1)
- NoSql (3)
- win10 (1)
- win10网络 (2)
- 底层 (3)
- 数据库 (0)
最新评论
-
kabuto_v:
请问那种图,uml图是怎么画出来的呢?是您自己手工画的,还是有 ...
FastJSON 序列化、反序列化实现 -
梦行Monxin商城系统:
电商实例、业务并发、网站并发及解决方法 -
rockethj8:
client 㓟有一个参数是可以忽略一些URL 不进行验证登录 ...
SSO 之 (单点登录)实施中遇到的几个问题 -
mengxiangfeiyan:
好啊。。。。。
Oracle删除表,删除数据以及恢复数据、利用现有表创建新表
http://fanmingchao111-163-com.iteye.com/blog/984836
单例模式完全解析
本文将探讨单例模式的各种情况,并给出相应的建议。单例模式应该是设计模式中比较简单的一个,但是在多线程并发的环境下使用却是不那么简单了。
首先看最原始的单例模式。
1 package xylz.study.singleton;
2
3 public class Singleton {
4
5 private static Singleton instance = null;
6
7 private Singleton() {
8 }
9
10 public static Singleton getInstance() {
11 if (instance == null) {
12 instance = new Singleton();
13 }
14 return instance;
15 }
16 }
17
显然这个写法在单线程环境下非常好,但是多线程会导致多个实例出现,这个大家都能理解。
最简单的改造方式是添加一个同步锁。
1 package xylz.study.singleton;
2
3 public class SynchronizedSingleton {
4
5 private static SynchronizedSingleton instance = null;
6
7 private SynchronizedSingleton() {
8 }
9
10 public static synchronized SynchronizedSingleton getInstance() {
11 if (instance == null) {
12 instance = new SynchronizedSingleton();
13 }
14 return instance;
15 }
16 }
17
显然上面的方法避免了并发的问题,但是由于我们只是在第一次构造对象的时候才需要同步,以后就不再需要同步,所以这里不可避免的有性能开销。于是将锁去掉采用静态的属性来解决同步锁的问题。
1 package xylz.study.singleton;
2
3 public class StaticSingleton {
4
5 private static StaticSingleton instance = new StaticSingleton();
6
7 private StaticSingleton() {
8 }
9
10 public static StaticSingleton getInstance() {
11 return instance;
12 }
13 }
14
上面的方法既没有锁又解决了性能问题,看起来已经满足需求了。但是追求“完美”的程序员想延时加载对象,希望在第一次获取的时候才构造对象,于是大家非常聪明的进行改造,也即非常出名的双重检查锁机制出来了。
1 package xylz.study.singleton;
2
3 public class DoubleLockSingleton {
4
5 private static DoubleLockSingleton instance = null;
6
7 private DoubleLockSingleton() {
8 }
9
10 public static DoubleLockSingleton getInstance() {
11 if (instance == null) {
12 synchronized (DoubleLockSingleton.class) {
13 if (instance == null) {
14 instance = new DoubleLockSingleton();
15 }
16 }
17 }
18 return instance;
19 }
20 }
21
双重锁机制看起来非常巧妙的避免了上面的问题。但是真的是这样的吗?文章《双重检查锁定及单例模式》中谈到了非常多演变的双重锁机制带来的问题,包括比较难以理解的指令重排序机制等。总之就是双重检查锁机制仍然对导致错误问题而不是性能问题。
一种避免上述问题的解决方案是使用volatile关键字,此关键字保证对一个对象修改后能够立即被其它线程看到,也就是避免了指令重排序和可见性问题。参考文章
指令重排序与happens-before法则。
所以上面的写法就变成了下面的例子。
package xylz.study.singleton;
public class DoubleLockSingleton {
private static volatile DoubleLockSingleton instance = null;
private DoubleLockSingleton() {
}
public static DoubleLockSingleton getInstance() {
if (instance == null) {
synchronized (DoubleLockSingleton.class) {
if (instance == null) {
instance = new DoubleLockSingleton();
}
}
}
return instance;
}
}
于是继续改造,某个牛人利用JVM的特性来解决上述问题,具体哪个牛人我忘记了,但是不是下面文章的作者。
(1)《Java theory and practice: Fixing the Java Memory Model, Part 2》
(2)《Initialize-On-Demand Holder Class and Singletons》
1 package xylz.study.singleton;
2
3 public class HolderSingleton {
4
5 private static class HolderSingletonHolder {
6
7 static HolderSingleton instance = new HolderSingleton();
8 }
9
10 private HolderSingleton() {
11 //maybe throw an Exception when doing something
12 }
13
14 public static HolderSingleton getInstance() {
15 return HolderSingletonHolder.instance;
16 }
17 }
18
上述代码看起来解决了上面单例模式遇到的所有问题,而且实际上工作的很好,没有什么问题。但是却有一个致命的问题,如果第11行抛出了一个异常,也就是第一次构造函数失败将导致永远无法再次得到构建对象的机会。
使用下面的代码测试下。
1 package xylz.study.singleton;
2
3 public class HolderSingletonTest {
4
5 private static class HolderSingletonHolder {
6
7 static HolderSingletonTest instance = new HolderSingletonTest();
8 }
9
10 private static boolean init = false;
11
12 private HolderSingletonTest() {
13 //maybe throw an Exception when doing something
14 if(!init) {
15 init=true;
16 throw new RuntimeException("fail");
17 }
18 }
19
20 public static HolderSingletonTest getInstance() {
21 return HolderSingletonHolder.instance;
22 }
23 public static void main(String[] args) {
24 for(int i=0;i<3;i++) {
25 try {
26 System.out.println(HolderSingletonTest.getInstance());
27 } catch (Exception e) {
28 System.err.println("one->"+i);
29 e.printStackTrace();
30 }catch(ExceptionInInitializerError err) {
31 System.err.println("two->"+i);
32 err.printStackTrace();
33 }catch(Throwable t) {
34 System.err.println("three->"+i);
35 t.printStackTrace();
36 }
37 }
38 }
39 }
40
很不幸将得到以下输出:
1 two->0
2 java.lang.ExceptionInInitializerError
3 at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
4 at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
5 Caused by: java.lang.RuntimeException: fail
6 at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
7 at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
8 at xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
9 2 more
10 three->1
11 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
12 at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
13 at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
14 three->2
15 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
16 at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
17 at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
18
很显然我们想着第一次加载失败第二次能够加载成功,非常不幸,JVM一旦加载某个类失败将认为此类的定义有问题,将来不再加载,这样就导致我们没有机会再加载。目前看起来没有办法避免此问题。如果要使用JVM的类加载特性就必须保证类加载一定正确,否则此问题将比并发和性能更严重。如果我们的类需要初始话那么就需要想其它办法避免在构造函数中完成。看起来像是又回到了老地方,难道不是么?
总之,结论是目前没有一个十全十美的单例模式,而大多数情况下我们只需要满足我们的需求就行,没必有特意追求最“完美”解决方案。
单例模式完全解析
本文将探讨单例模式的各种情况,并给出相应的建议。单例模式应该是设计模式中比较简单的一个,但是在多线程并发的环境下使用却是不那么简单了。
首先看最原始的单例模式。
1 package xylz.study.singleton;
2
3 public class Singleton {
4
5 private static Singleton instance = null;
6
7 private Singleton() {
8 }
9
10 public static Singleton getInstance() {
11 if (instance == null) {
12 instance = new Singleton();
13 }
14 return instance;
15 }
16 }
17
显然这个写法在单线程环境下非常好,但是多线程会导致多个实例出现,这个大家都能理解。
最简单的改造方式是添加一个同步锁。
1 package xylz.study.singleton;
2
3 public class SynchronizedSingleton {
4
5 private static SynchronizedSingleton instance = null;
6
7 private SynchronizedSingleton() {
8 }
9
10 public static synchronized SynchronizedSingleton getInstance() {
11 if (instance == null) {
12 instance = new SynchronizedSingleton();
13 }
14 return instance;
15 }
16 }
17
显然上面的方法避免了并发的问题,但是由于我们只是在第一次构造对象的时候才需要同步,以后就不再需要同步,所以这里不可避免的有性能开销。于是将锁去掉采用静态的属性来解决同步锁的问题。
1 package xylz.study.singleton;
2
3 public class StaticSingleton {
4
5 private static StaticSingleton instance = new StaticSingleton();
6
7 private StaticSingleton() {
8 }
9
10 public static StaticSingleton getInstance() {
11 return instance;
12 }
13 }
14
上面的方法既没有锁又解决了性能问题,看起来已经满足需求了。但是追求“完美”的程序员想延时加载对象,希望在第一次获取的时候才构造对象,于是大家非常聪明的进行改造,也即非常出名的双重检查锁机制出来了。
1 package xylz.study.singleton;
2
3 public class DoubleLockSingleton {
4
5 private static DoubleLockSingleton instance = null;
6
7 private DoubleLockSingleton() {
8 }
9
10 public static DoubleLockSingleton getInstance() {
11 if (instance == null) {
12 synchronized (DoubleLockSingleton.class) {
13 if (instance == null) {
14 instance = new DoubleLockSingleton();
15 }
16 }
17 }
18 return instance;
19 }
20 }
21
双重锁机制看起来非常巧妙的避免了上面的问题。但是真的是这样的吗?文章《双重检查锁定及单例模式》中谈到了非常多演变的双重锁机制带来的问题,包括比较难以理解的指令重排序机制等。总之就是双重检查锁机制仍然对导致错误问题而不是性能问题。
一种避免上述问题的解决方案是使用volatile关键字,此关键字保证对一个对象修改后能够立即被其它线程看到,也就是避免了指令重排序和可见性问题。参考文章
指令重排序与happens-before法则。
所以上面的写法就变成了下面的例子。
package xylz.study.singleton;
public class DoubleLockSingleton {
private static volatile DoubleLockSingleton instance = null;
private DoubleLockSingleton() {
}
public static DoubleLockSingleton getInstance() {
if (instance == null) {
synchronized (DoubleLockSingleton.class) {
if (instance == null) {
instance = new DoubleLockSingleton();
}
}
}
return instance;
}
}
于是继续改造,某个牛人利用JVM的特性来解决上述问题,具体哪个牛人我忘记了,但是不是下面文章的作者。
(1)《Java theory and practice: Fixing the Java Memory Model, Part 2》
(2)《Initialize-On-Demand Holder Class and Singletons》
1 package xylz.study.singleton;
2
3 public class HolderSingleton {
4
5 private static class HolderSingletonHolder {
6
7 static HolderSingleton instance = new HolderSingleton();
8 }
9
10 private HolderSingleton() {
11 //maybe throw an Exception when doing something
12 }
13
14 public static HolderSingleton getInstance() {
15 return HolderSingletonHolder.instance;
16 }
17 }
18
上述代码看起来解决了上面单例模式遇到的所有问题,而且实际上工作的很好,没有什么问题。但是却有一个致命的问题,如果第11行抛出了一个异常,也就是第一次构造函数失败将导致永远无法再次得到构建对象的机会。
使用下面的代码测试下。
1 package xylz.study.singleton;
2
3 public class HolderSingletonTest {
4
5 private static class HolderSingletonHolder {
6
7 static HolderSingletonTest instance = new HolderSingletonTest();
8 }
9
10 private static boolean init = false;
11
12 private HolderSingletonTest() {
13 //maybe throw an Exception when doing something
14 if(!init) {
15 init=true;
16 throw new RuntimeException("fail");
17 }
18 }
19
20 public static HolderSingletonTest getInstance() {
21 return HolderSingletonHolder.instance;
22 }
23 public static void main(String[] args) {
24 for(int i=0;i<3;i++) {
25 try {
26 System.out.println(HolderSingletonTest.getInstance());
27 } catch (Exception e) {
28 System.err.println("one->"+i);
29 e.printStackTrace();
30 }catch(ExceptionInInitializerError err) {
31 System.err.println("two->"+i);
32 err.printStackTrace();
33 }catch(Throwable t) {
34 System.err.println("three->"+i);
35 t.printStackTrace();
36 }
37 }
38 }
39 }
40
很不幸将得到以下输出:
1 two->0
2 java.lang.ExceptionInInitializerError
3 at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
4 at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
5 Caused by: java.lang.RuntimeException: fail
6 at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
7 at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
8 at xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
9 2 more
10 three->1
11 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
12 at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
13 at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
14 three->2
15 java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
16 at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
17 at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
18
很显然我们想着第一次加载失败第二次能够加载成功,非常不幸,JVM一旦加载某个类失败将认为此类的定义有问题,将来不再加载,这样就导致我们没有机会再加载。目前看起来没有办法避免此问题。如果要使用JVM的类加载特性就必须保证类加载一定正确,否则此问题将比并发和性能更严重。如果我们的类需要初始话那么就需要想其它办法避免在构造函数中完成。看起来像是又回到了老地方,难道不是么?
总之,结论是目前没有一个十全十美的单例模式,而大多数情况下我们只需要满足我们的需求就行,没必有特意追求最“完美”解决方案。
发表评论
-
面相接口(抽象)编程
2016-01-18 15:38 1045http://liuwaner118.iteye.com/ ... -
java NIO实战
2016-01-13 09:57 945一、NIO应用场景 1、写爬虫 2、做通讯服务器就经 ... -
电商实例、业务并发、网站并发及解决方法
2016-01-05 16:09 3931一、怎么防止多用户同一时间抢购同一商品,防止高并发同时下单 ... -
设计模式 之 策略模式
2013-08-16 08:39 744目录[隐藏 ] 1 策略模式 ... -
Executor
2013-06-11 01:17 2112http://www.iteye.com/topic/69 ... -
并发编程Execute
2013-06-05 00:03 1155并发编程-Executor框架该帖已经被评为精华帖 Execu ... -
Template模式
2013-05-26 22:38 859Java事务处理全解析(五)—— Template模式 在本系 ... -
Java多线程程序设计详细解析
2013-05-25 11:02 0Java多线程程序设计详细 ... -
Java多线程编程的常见陷阱
2012-12-05 10:47 889http://www.360doc.com/resaveArt ... -
Servlet及JSP中的多线程同步问题
2012-11-25 23:22 801http://www.360doc.com/conte ... -
构建Java并发模型框架
2012-11-14 00:06 940http://www.ibm.com/develope ... -
Java并发中的任务执行
2012-11-12 11:07 1020http://www.360doc.com/con ... -
抽象工厂模式解析例子
2012-09-11 22:54 888http://tianli.blog.51cto ... -
外观模式(Facade)解析例子
2012-09-11 22:42 1037http://tianli.blog.51cto ... -
Null Object 模式
2012-09-05 22:51 941Null Object 模式用在下面两个场景里确实有作 ... -
多线程开发
2012-09-03 23:12 0参考资料: 参考1:java开发实战经典 ... -
原型模式(Prototype)解析例子
2012-09-03 22:46 1045http://tianli.blog.51cto ... -
单例模式(singleton)解析例子
2012-09-03 22:41 1002http://tianli.blog.51cto ... -
观察者模式(Observer)解析例子
2012-09-03 22:40 837http://tianli.blog.51cto.co ... -
Java:应用Observer接口实践Observer模式
2012-09-03 22:13 942http://zhangjunhd.blog.51c ...
相关推荐
* 单例设计模式-饿汉式// 构造器* 返回实例对象* 饿汉式避免了并发安全问题,但是却无法实现lazyLoad饿汉式面临的问题:对象无法实现lazy-load
细心整合和单例模式和工厂模式的几种模型,懒汉式,饿汉式,如何并发操作模式,等都有详细讲解
├─11.20 单例设计模式和Spring家族介绍-1.mp4 ├─11.20 单例设计模式和Spring家族介绍-2.mp4 ├─11.20 单例设计模式和Spring家族介绍-3.mp4 ├─11.22 手写Spring IOC模块.mp4 ├─11.25 手写spring ioc模块及...
列举一些常见的设计模式。 什么是Java中的单例模式?如何实现线程安全的单例模式? 什么是Java中的生命周期回调方法?列举一些常见的生命周期回调方法。 什么是Java中的注解处理器?如何自定义和使用注解处理器?...
单例模式的实现机制,并发情况下的单例模式的存在问题及解决方法,无锁的线程安全单例模式
最近在学习多线程相关知识,同时加深了对单例的理解,从并发的角度学习到了不同的单列模式,提供出来供大家一起学习
设计模式: 探讨常见的设计模式,如工厂模式、单例模式、观察者模式等,以及如何在Python中应用这些模式。 测试和调试: 介绍高级的测试技术和调试工具,以确保代码的质量和可维护性。 性能优化: 提供关于Python...
单例模式应该是设计模式中比较简单的一个,也是非常常见的,但是在多线程并发的环境下使用却是不那么简单了,今天给大家分享一个我在开发过程中遇到的单例模式的应用。 首先我们先来看一下单例模式的定义: 一个类有...
Java代码积累:并发 设计模式 数据结构 使用容器 实用 类 基础知识 并发性 演示线程的生命周期 生产者-消费者 设计模式参考《大话设计模式》 工厂简单模式 创造型模式 工厂方法模式 抽象工厂模式 原型模式 建造者...
对于设计模式,最熟知和最常用的不外乎单例模式和工厂模式,对于单例模式如果编写不严谨的话也存在安全漏洞问题,这个击穿单例模式的代码很形象的说明了这个问题,其中包含如何使用现成并发技术,欢迎大家下载学习。
Go设计模式01-单例模式 Go设计模式02-工厂模式&DI容器 Go设计模式03-建造者模式 Go设计模式04-原型模式 Go设计模式05-创建型模式总结 结构型 Go设计模式06-代理模式(generate实现类似动态代理) Go设计模式07-桥接...
单例模式 饿汉式: /** * 饿汉式单例(提前把对象创建) * 可能会浪费空间,提前把对象创建好了,但是不一定会用。 */ public class Hungry { private Hungry(){ } private final static Hungry HUNGRY=new ...
第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...
5并行模式与算法 5.1探讨单例模式 5.3生产者-消费者模式 5.5future模式 5.2不变模式 5.4高性能的生产者-消费者模式:无锁的实现 5.6并行流水线 01 02 03 04 05 06 实战Java高并发程序设计(第2版)PPT模板全文共25...
单例模式是所有设计模式中比较简单的一类,其定义如下:Ensure a class has only one instance, and provide a global point of access to it.(保证某一个类只有一个实例,而且在全局只有一个访问点) 单例模式的...
该项目主要涉及C/C++、数据结构(链表、哈希桶)、操作系统内存管理、单例模式、多线程、互斥锁等方面的技术。 项目详解:https://blog.csdn.net/chenlong_cxy/article/details/122819562?spm=1001.2014.3001.5502
Future设计模式实现(实现类似于JDK提供的Future).mp4 Future源码解读.mp4 ForkJoin框架详解.mp4 同步容器与并发容器.mp4 并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用....
第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...
第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...