设计模式之单例模式(Java)
来源:互联网 发布:县医院 网络图片图 编辑:程序博客网 时间:2024/06/06 19:13
设计模式之单例模式实现
单例模式是确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免多地同时修改状态。单例模式有以下特点:1、单例类只能有一个实例。2、单例类必须自己创建自己的唯一实例。3、单例类必须给所有其他对象提供这一实例。
Java 编写单例模式有七种写法
第一种 懒汉式单例
package com.xiaokai.constructor.singleton;/** * Created by Administrator on 2016/8/4. */public class Singleton1 { //私有静态实例(全局共享) private static Singleton1 instance; //私有构造器(外部不能实例化) private Singleton1() { } //获取单例 public static Singleton1 getInstance() { //如果没有实例化,先实例化返回 if (instance == null) { instance = new Singleton1(); } return instance; }}
单元测试类
package com.xiaokai.constructor.singleton;import org.junit.Assert;import org.junit.Test;/** * Created by Administrator on 2016/8/4. */public class Singleton1Test { @Test public void getInstance() throws Exception { Singleton1 singleton1 = Singleton1.getInstance(); Singleton1 singleton11 = Singleton1.getInstance(); Assert.assertSame(singleton1, singleton1); }}
说明:此懒汉式单例在多线程不能正常工作(非线程安全),并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全。
1、在getInstance方法上加同步
//在获取单例方法上加锁public static synchronized Singleton1 getInstance() { if (single == null) { single = new Singleton1(); } return single;}
这种写法虽然能够在多线程中很好的工作,但是,他的效率很低,因为单例已经存在的情况下是不需要同步的。
2、双重检查锁定
//加锁前先判单例是否已经存在,只有在不存的时候加锁实例化出来一个单例public static Singleton1 getInstance() { if (singleton == null) { //注意是类级锁 synchronized (Singleton1.class) { if (singleton == null) { singleton = new Singleton1(); } } } return singleton; }
注意:双重检查锁定在JDK1.5之后,才能够正常达到单例效果。
3、使用静态内部类实现单例模式(推荐)
public class Singleton { //定义私有静态内部类 private static class SingletonHolder { //加载时实例化单例,static final类型 private static final Singleton INSTANCE = new Singleton(); } //私有构造器 private Singleton (){} //通过调用getInstance实例化静态内部类,返回静态内部类加载时实例化的单例 public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
内部类方式既实现了线程安全,又避免了同步带来的性能影响。这种方式利用了classloder的机制来保证初始化instance时只有一个线程,这种方式情况下,Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance。如果实例化instance很消耗资源,让他延迟加载是很有帮助的。
第二种 饿汉式单例
package com.xiaokai.constructor.singleton;/** * Created by Administrator on 2016/8/4. */public class Singleton2 { private Singleton2() { } //在类初始化时,实例化单例 private static final Singleton2 instance = new Singleton2(); //静态工厂方法 public static Singleton2 getInstance() { return instance; }}
单元测试类
package com.xiaokai.constructor.singleton;import org.junit.Assert;import org.junit.Test;import static org.junit.Assert.*;/** * Created by Administrator on 2016/8/4. */public class Singleton2Test { @Test public void getInstance() throws Exception { Singleton2 singleton1 = Singleton2.getInstance(); Singleton2 singleton11 = Singleton2.getInstance(); Assert.assertSame(singleton1, singleton1); }}
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
第三种 枚举式单例(强烈推荐)
package com.xiaokai.constructor.singleton;/** * Created by Administrator on 2016/8/4. */public enum Singleton3 { INSTANCE; public void whateverMethod() { }}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。有两个问题需要注意: 1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。 2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。解决第一个问题的方法是重写Object的getClass方法。
private static Class getClass(String classname) throws ClassNotFoundException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //先判空,再加载单例 if (classLoader == null) classLoader = Singleton3.class.getClassLoader(); return (classLoader.loadClass(classname)); }
解决第二个问题的方法是实现序列化接口后重写readResolve方法。
public class Singleton implements java.io.Serializable { public static Singleton INSTANCE = new Singleton(); protected Singleton() { } private Object readResolve() { return INSTANCE; }}
总结
三类:懒汉(懒汉又有双重校验锁,静态内部类),饿汉,枚举。懒汉:需要加锁才能实现多线程同步,但是效率会降低。优点是延时加载。双重校验锁:麻烦,在当前Java内存模型中不一定都管用,某些平台和编译器甚至是错误的,在JDK1.5之后,才能够正常达到单例效果。。静态内部类:延迟加载,减少内存开销。因为用到的时候才加载,避免了静态field在单例类加载时即进入到堆内存的permanent代而永远得不到回收的缺点(大多数垃圾回收算法是这样)。饿汉:因为加载类的时候就创建实例,所以线程安全(多个ClassLoader存在时例外)。缺点是不能延时加载。枚举:很好,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是失去了类的一些特性,没有延迟加载。
1 0
- java设计模式之单例模式
- Java模式设计之单例模式
- Java模式设计之单例模式
- Java模式设计之单例模式
- java设计模式之单例模式
- Java模式设计之单例模式
- Java模式设计之单例模式
- Java设计模式之单例模式
- Java设计模式之单例模式
- Java设计模式之单例模式
- Java设计模式之单例模式
- Java模式设计之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- java设计模式之单例模式
- android 事件的传递与消费
- 使用tmpfs作缓存、临时文件存储加速
- Linux下实现USB口的热插拔
- 生成分页代码
- 欢迎使用CSDN-markdown编辑器
- 设计模式之单例模式(Java)
- 11111
- windows平台下安装python的setuptools工具
- SI与EMI(二)
- iOS GCD实现
- 如何用java 5分钟实现一个最简单的mysql代理服务器?
- 调用系统功能,邮件发送方式,各种文件打开方法-Intent的详解
- POJ 2186
- Office 可以插入和保存的图形文件类型