java 设计模式 其一 单例模式
来源:互联网 发布:centos 五笔输入法 编辑:程序博客网 时间:2024/05/19 00:11
单例模式
转载出处(侵删)
http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
有的时候,我们在全局中,为了不制造多个实例,避免一些资源问题的产生,只需要实例一次类的对象,用于:线程池、缓存、对话框、处理偏好设置和注册表对象、日志对象、驱动程序等等。
一
那我们要如何控制类只能实例化一个对象?
不妨看看我们平时是怎么使用类进行实例化的
public class Person{}Person p = new Person();
二
但是现在,说好的是要用单例模式,就要有所不同。
我们要达到的效果是:通过某种方法,只能实例化一次对象。那么根据以往所学到知识,static关键字可以满足这一目的(由于static修饰的方法,不依赖任何对象就可以访问,static是类的一部分)
//通过静态方法实现实例化对象public class Person{ private static Person p; public static Person getInstance(){ return p = new Person(); }}//这样我们创建对象的时候,通过类静态方法获取实例Person p = Person.getInstance();
三
等等,好像有什么不对。
没错,这样,你依旧能够使用无参构造器去创建一个对象,或许我们可以修改无参构造器来禁止创建对象。
public class Person{ //私有无参构造器禁止创建对象。 private Person(){}; private static Person p; public static Person getInstance(){ return p = new Person(); }}
四
当然,如果这个对象是被创建了的,我们可以使用lazy loading,以防止在线程中创建了还再创建一次
//第一种 懒汉,线程不安全public class Person{ private Person(){}; private static Person p; public static Person getInstance(){ //存在对象时,禁止创建对象 if(p==null) p = new Person() return p; }}
初步就构建好了一个单例的例子了。
五
实际开发中,这往往不能满足我们的要求。如果遇到多线程怎么办?或许能上锁改装改装..
//第二种 懒汉,线程安全public class Person{ //无参构造器来禁止创建对象。 private Person(){}; private static Person p; public static synchronized Person getInstance(){ if(p==null) p = new Person() return p; }}
看起来貌似不错,但是实际上效率却很低,我们要拿到锁去调用方法。
有没有方法不加锁的呢?
六
在1.5 之后 我们可以通过下面的方式加锁。
//第三种 双重加锁校验public class Person{ private Person(){}; private volatile static Person p; //移除方法同步锁 public static Person getInstance(){ if(p==null) //如果为空,去拿锁,拿锁的过程需要等待,可能会在这个过程产生实例 synchronized(Person.class){ //拿锁之后,再次判断对象是否被创建 if(p==null) p = new Person() } return p; }}
这样总算大工告成了。这样就能安心在多线程中得到单例对象了。
七
什么?你觉得代码太烦。或许你能试试下面的方法
//第四种 饿汉public class Person{ private Person(){}; //利用classloder机制 避免多线程的同步问题。 private static Person p =new Person(); public static Person getInstance(){ return p; }}
但是这种方法在类装载时进行实例化,虽然多少情况下是调用getInstance方法,不过不能排除有其他的静态的方法导致类装载,这个时候就没有达到 Lazy loading的效果。
PS:什么?你问我什么是“利用classloder机制 避免多线程的同步问题”?
好吧,根据《深入理解java虚拟机》中,大致是说,虚拟机会保证类的静态方法实例在多线程中正确地加锁、 同步,保证只有一个类能对其进行操作。所以可以使用上面的说法
八
所以有了下面使用静态内部类的方式
//第五种 静态内部类public class Person{ private Person(){}; //静态内部类 private static final PersonHolder{ private static final Person pInstance =new Person(); } public static Person getInstance(){ return PersonHolder.pInstance; }}
相比第四种 饿汉加载模式,这种即时在类被装载的时候,pInstance 也不一定初始化,因为PersonHolder没有被主动使用,只有显示的通过调用的getInstance方法,才会去装载 PersonHolder类,从而实例化 instance。
九
当然还有另外一种方式 《Effective java》 大佬 Josh Bloch 提倡,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒。(只是实际中很少人有这么写)
//第六种 枚举方式class Resource{}public enum SomeThing { INSTANCE; private Resource instance; SomeThing() { instance = new Resource(); } public Resource getInstance() { return instance; }}//当要实例化的时候,只需要Resource r = SomeThing.INSTANCE.getInstance();
为什么说这样的单例能被保证?
枚举的构造方法是私有的,在我们在访问枚举实例时会执行构造方法,
同时每个枚举的实例都是 static finla 类型的,也就表明只能被实例化一次。在调用构造方法时,enum中的实例被保证实只会被例化一次。所以我们的INSTANCE也被保证实例化一次。
枚举还提供了序列化机制
public abstract class Enum<E extends Enum<E>> implements Comparable<E>,Serializable
十
有两个问题需要注意:
1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
解决方式
private static Class getClass(String classname) throws ClassNotFoundException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if(classLoader == null) classLoader = Singleton.class.getClassLoader(); return (classLoader.loadClass(classname)); } }
2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例
解决方式
public class Singleton implements java.io.Serializable { public static Singleton INSTANCE = new Singleton(); protected Singleton() { } private Object readResolve() { return INSTANCE; } }
最后
日常使用中,可以使用 第三种 和 第五种 ,第六种还没见人用过
(笔者代码看得太少的缘故吧,请多多指教啦 ╮(╯▽╰)╭ )
- java 设计模式 其一 单例模式
- Java设计模式其一【单例设计模式】
- gcd实现单例模式其一
- java 设计模式,单例设计模式
- java设计模式-单例设计模式
- java设计模式-单例设计模式
- Java设计模式 单例设计模式
- Java设计模式------单例设计模式
- java设计模式----->单例设计模式
- java设计模式:单例设计模式
- java设计模式-----单例设计模式
- Java设计模式----单例设计模式
- java设计模式-单例设计模式
- java设计模式:单例设计模式
- java 设计模式-单例设计模式
- Java设计模式--单例设计模式
- java设计模式---单例设计模式
- Java设计模式-单例设计模式
- 偶串
- SQL查询前几条数据的方法
- matlab下PC2PC网口通信操作
- 彩色瓷砖
- 对于HTML5表单新增元素的小练习
- java 设计模式 其一 单例模式
- Neutron安全组分析(二)
- android logger 简单,漂亮和强大的记录器
- 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(四)
- 鼠标的常用事件
- 知识迁移能力-和为S的两个数字
- 知识迁移能力-二叉树的深度
- neutron安全组分析(三)
- win10安装配置maven环境