浅析Java设计模式 - 单例模式
2016-03-09 14:12
603 查看
以下是三种单例模式的代码实现,前两者用的比较多 (言外之意 最后一种可以忽略)
单例模式
在了解饿汉与懒汉的区别前 先了解下什么是线程安全:
假如你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时执行这段代码,如果每次执行的结果和单线程中执行的结果一致 且 其它变量的值也和预期的一致 这就是线程安全
换句话说:
一个类或者程序提供的接口对于线程来说是原子操作,或者多个线程之间切换不会导致该接口的执行结果存在差异性 也就是说我们不用考虑同步的问题 那就是线程安全的
饿汉式和懒汉式区别:
从名字上来说,饿汉和懒汉:
饿汉是当类加载时就把单例初始化完成,保证getInstance的时候,单例是已经存在的了
而懒汉比较懒,只有当调用getInstance的时候,才会去初始化这个单例;
从线程是否安全角度讲:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题
懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别;
从资源加载和性能的角度讲:
饿汉式在类加载的同时就实例化一个静态对象出来,不管之后是否用到这个静态对象,都会占据一定的内存,但是相应的 在第一次调用时的速度也会更快,因为资源已经初始化好了;
至于以上1,2,3 这三种实现又有些区别
第一种,在方法调用上加了同步,虽然线程安全了,但每次都要同步,会影响性能,毕竟大部分的情况下是不需要同步的
第二种,在getInstance方法中做了两次null检查,确保了只有第一次调用该静态对象的时候才会同步,这样也是线程安全的,同时避免了每次调用时的同步损耗;
第三种,利用了classloader机制,保证初始化instance时只有一个线程 所以是线程安全的 同时没有性能损耗
package com.signle; import java.util.HashMap; import java.util.Map; /** * * @title 单例模式 * @Copyright Copyright (c)2016年3月9日 * @Company CTC * @version 1.0 * @author ejokovic * @time 上午11:21:53 * @package * com.signle * */ public class TestSignle { public static void main(String[] args) { Singleton s = Singleton.getInstance(); Singleton s1 = Singleton.getInstance(); //System.out.println(s==s1); SigletionC singleC = SigletionC.getInstance(null); System.out.println(singleC.about()); } } /** * 懒汉式 * 是线程不安全的并发环境下很可能出现多个Singleton实例,要实现线程安全 * @title * @Copyright Copyright (c)2016年3月9日 * @Company CTC * @version 1.0 * @author ejokovic * @time 上午11:23:44 * @package * com.signle * */ class Singleton{ private Singleton(){} private static Singleton Singleton; public static Singleton getInstance(){ if(Singleton==null){ Singleton = new Singleton(); } return Singleton; } } /** * 解决懒汉式单例线程安全(一) * 在getInstance方法上加同步锁 * @title * @Copyright Copyright (c)2016年3月9日 * @Company CTC * @version 1.0 * @author ejokovic * @time 上午11:31:45 * @package * com.signle * */ class Singleton2{ private Singleton2(){} private static Singleton2 Singleton; public static synchronized Singleton2 getInstance(){ if(Singleton==null){ Singleton = new Singleton2(); } return Singleton; } } /** * 解决懒汉式单例线程安全(二) * 双重检查锁定 * @title * @Copyright Copyright (c)2016年3月9日 * @Company CTC * @version 1.0 * @author ejokovic * @time 上午11:34:42 * @package * com.signle * */ class Singleton3{ private Singleton3(){} private static Singleton3 Singleton; public static synchronized Singleton3 getInstance(){ if(Singleton==null){ synchronized (Singleton3.class){ if(Singleton==null){ Singleton = new Singleton3(); } } } return Singleton; } } /** * 解决懒汉式单例线程安全(二) * 静态内部类 * 这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。 * @title * @Copyright Copyright (c)2016年3月9日 * @Company CTC * @version 1.0 * @author ejokovic * @time 下午12:06:54 * @package * com.signle * */ class Singleton4{ private static class LazyHolder{ private static final Singleton4 INSTANCE = new Singleton4(); } private Singleton4(){} public static synchronized Singleton4 getInstance(){ return LazyHolder.INSTANCE; } } /** * 饿汉式单例类.在类初始化时,已经自行实例化 * @title * @Copyright Copyright (c)2016年3月9日 * @Company CTC * @version 1.0 * @author ejokovic * @time 下午12:12:23 * @package * com.signle * */ class SigletionA{ private SigletionA(){} private static final SigletionA sigletionA = new SigletionA(); public static SigletionA getInstance(){ return sigletionA; } } //登记式单例 class SigletionC{ private static Map<String,SigletionC> map = new HashMap<String,SigletionC>(); static{ SigletionC single = new SigletionC(); map.put(single.getClass().getName(), single); } //保护的默认构造子 protected SigletionC(){} //静态工厂方法,返还此类惟一的实例 public static SigletionC getInstance(String name) { if(name == null) { name = SigletionC.class.getName(); System.out.println("name == null"+"--->name="+name); } if(map.get(name) == null) { try { map.put(name, (SigletionC) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); } //一个示意性的商业方法 public String about() { return "Hello, I am RegSingleton."; } }
单例模式
在了解饿汉与懒汉的区别前 先了解下什么是线程安全:
假如你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时执行这段代码,如果每次执行的结果和单线程中执行的结果一致 且 其它变量的值也和预期的一致 这就是线程安全
换句话说:
一个类或者程序提供的接口对于线程来说是原子操作,或者多个线程之间切换不会导致该接口的执行结果存在差异性 也就是说我们不用考虑同步的问题 那就是线程安全的
饿汉式和懒汉式区别:
从名字上来说,饿汉和懒汉:
饿汉是当类加载时就把单例初始化完成,保证getInstance的时候,单例是已经存在的了
而懒汉比较懒,只有当调用getInstance的时候,才会去初始化这个单例;
从线程是否安全角度讲:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题
懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别;
从资源加载和性能的角度讲:
饿汉式在类加载的同时就实例化一个静态对象出来,不管之后是否用到这个静态对象,都会占据一定的内存,但是相应的 在第一次调用时的速度也会更快,因为资源已经初始化好了;
至于以上1,2,3 这三种实现又有些区别
第一种,在方法调用上加了同步,虽然线程安全了,但每次都要同步,会影响性能,毕竟大部分的情况下是不需要同步的
第二种,在getInstance方法中做了两次null检查,确保了只有第一次调用该静态对象的时候才会同步,这样也是线程安全的,同时避免了每次调用时的同步损耗;
第三种,利用了classloader机制,保证初始化instance时只有一个线程 所以是线程安全的 同时没有性能损耗
相关文章推荐
- C# 加密(Encrypt) 解密(Decrypt) 操作类 java与 C# 可以相互加密解密
- Java--编码集与序列化
- JavaFX设置面板不捕获鼠标事件(透明或不计算边界)
- eclipse部署web项目中各种错误总汇
- Eclipse快捷键大全(转载)
- Spring基于ThreadLocal的“资源-事务”线程绑定设计的缘起
- Java多线程之synchronized和Lock
- Java线程stop和suspend的废弃
- Java多线程:用三个线程控制循环输出10次ABC
- maven 工程启动找不到 Spring ContextLoaderListener 的解决办法
- java笔试题(二)
- 跟着项目学javaweb(std):3(走进后台)
- java并发编程(一):计数器
- JavaEE(二)---Web 应用程序安全性问题及基本安全实施策略
- JavaEE(一)---Web服务编程,REST 与 SOAP
- Java异常处理详解
- java笔试题(一)
- openjdk和jdk区别
- 在Spring中调用基于CXF框架的webService
- SpringMVC+Spring+Hibernate+Maven+mysql整合