标签:设计模式
同步一个方法可能造成程序执行效率下降100倍.
静态初始化的控制权是在Java手上
一个类,一个责任 原则. ( 类应该做一件事,而且只做一件事) 但单件做了两件事, 一是 管理自己的实例(并提供全局访问变量) 二是在应用程序中担任角色,
(1). 私有构造器.不提供对外访问 (2). 静态方法对外提供类本身实例. (3) . 只有一个实例
单例模式: ( 管理共享资源 如数据库连接,或者线程池) MVC DAO Service 层
延迟实例化(懒汉式) 有线程安全问题 --lazy Instance
急切实例化 engerly (饿汉式)
1. OO原则:
(1) 封装变化 (2) 多用组合,少用继承 (3) 针对接口编程,不针对实现编程 (4) 为交互对象之间的松耦合设计而努力 (5) 类应该对扩展开放,对修改关闭 (6) 依赖抽象,不要依赖具体类.
2.OO模式: 单例模式: 确保一个类只有一个实例,并提供全局访问点.
> 延迟实例化: 有着线程安全问题
public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static synchronized Singleton getInstance(){ //解决了线程问题但却降低了效率. 去掉 synchronized 会有线程安全问题
if(uniqueInstance ==null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
---------------
用"双重检查加锁" 在getInstance()中减少使用同步
利用双重检查加锁(double-checked locking) ,首先检查是否实例已经创建了,如果尚未创建,"才"进行同步.这样一来,只有第一次会同步.
public class Singleton{
private volatile staitc Singleton uniqueInstance ;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance ==null){
synchronized(Singleton.class){ //因为是静态方法中
if(uniqueInstance ==null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
(1) volatitle 关键词确保,当uniqueInstance 变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance 变量.
(2) 如果性能是你关心的重点,那么这个做法可你帮你大大地减少getInstance()的时间耗费.
(3) 双重检查加锁不适用于 1.4及更早的版本的Java. 在之前版本许多JVM对volatile关键字的实现会导致双重检查加锁失效.
>. 急切实例化
(1). JVM 保证在任何线程访问Instance静态变量之前,一定先创建此实例.
public class Singleton{
private static Singleton uniqueInstance = new Singleton(); //这里可以用final修饰
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance;
}
}
(1). 当你需要控制实体个数时, 使用单例模式.
(2). 创建一个类, 把所有方法和变量定义为静态的, 把类直接当做一个单件.? (类的单件 and 对象的单件)
如果你的类自己给自足,而且不依赖于复杂的初始化,那么你可以这么做. 但是,因为静态初始化的控制权是在Java手上.这么做有可能导致混乱.特别是当许多类牵涉其中的时候,
这么做常常会造成一些微妙的,不容易发现的和初始化的次序有关的BUG. 除非你有绝对的必要使用类的单件.否则,还是建议使用对象的单件.比较保险
(3). 类加载器(classloader) 两个类加载器可能有机会各自创建自己的单件模式.?
是的. 每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器,可能会加载同一个类,从整个程序来看,同一个类会被加载多次. 如果这样的事情发生在单件上, 就会产生多个单件并存的怪异现象. 所以, 你的程序有多个类加载器又同时使用了单件模式,请小心, 有一个解决方法, 自行指定类加载器,并指定同一个类加载器.
(4). 全局变量 VS 单件模式
在Java中,全局变量基本上就是对 对象的静态引用. 在这样的情况下全局变量有一个缺点,我们已经提到了其中一个:急切实例化VS延迟实例化. 但我们要记住这个模式的目的: 确保类只有一个实例并提供全局访问. 全局变量可以提供全局访问, 但是不能确保只有一个实例. 全局变量也会变相鼓励开发人员,用许多全局变量指向许多小对象来选成命名空间(namespace)的污染. 单件不鼓励这样的现象, 但单件仍然可能被滥用.
高亮--全局变量缺点:如果将对象赋值给一个全局变量,那么你必须在程序一开始就创建好对象①,万一这个对象非常耗资源,而程序在这次的执行中又一直没用到它.
利用单件模式,我们可以需要时才创建对象.
① : 这其实和实现有关.有些JVM的实现是: 在用到的时候才创建对象.
(5) 谣传垃圾收集器会吃掉单件, 这过份夸大了!
在Java 1.2之前,, 垃圾收集器有个BUG , 会造成单件在没有全局的引用时被当作垃圾清除.也就是说,如果一个单件只有本单件类引用它本身,那么该单件,就会被当作垃圾清除.这造成让人困惑的BUG: 因为单件在被清除之后, 下次调用getInstance()会产生一个"全新的"单件. 对很多程序来说,这会造成让人困惑的行为,因为对象的实例变量值都不见了,一切回到最原始的设置(例如: 网络连接被重置).
Java 1.2以后,这个bug 已经被修正了,也不再需要一个全局引用来保护单件.
要点:
(1). 单件模式确保程序中一个类最多只有一个实例.
(2). 单件模式也提供 访问这个实例的全局点.
(3). 在Java中实现单件模式需要私有的构造器,一个静态方法和 一个静态变量.
(4). 确定在性能和资源上的限制,然后小心地选择适当的方案来实现单件.以解决多线程的问题(我们必须认定所有的程序是多线程的).
(5) . 如果不是采用第五版的Java 2, 双重检查加锁实现会失效.
(6). 如果使用多个类加载器,可能会导致单件失效而产生多个实例
(7). 如果使用 JVM 1.2或之前的版本, 你必须建立单件注册表,以免垃圾收集器将单件回收.
(0) volatitle 关键词 :
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
就跟C中的一样 禁止编译器进行优化~~~~
标签:设计模式
原文地址:http://blog.csdn.net/qq717559128/article/details/25919333