Singleton
1.//懒汉模式
//天生线程不安全,但是效率高
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getSingleton(){
if(singleton==null){
singleton=new Singleton();
System.out.println("实例化Singleton");
}
return singleton;
} } public class Test {
public static void main(String[] args) {
MyThread myThread1=new MyThread();
MyThread myThread2=new MyThread();
MyThread myThread3=new MyThread();
myThread1.start();
myThread2.start();
myThread3.start();
}
//因为继承Thread每次都会新创建一个任务,所以可以达到效果,而使用Runnable不能够达到效果
public static class MyThread extends Thread{
int count=0;
@Override
public void run() {
while(count<100){
count++;
Singleton.getSingleton();
}
}
}
}
此程序输出的结果为:
实例化Singleton
实例化Singleton
说明此时实例化了两个Singleton,线程不安全!
2.在上面做一点改动
//改良版懒汉模式
//线程安全,但是效率低
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getSingleton(){
if(singleton==null){
singleton=new Singleton();
System.out.println("实例化Singleton");
}
return singleton;
} } public class Test {
public static void main(String[] args) {
MyThread myThread1=new MyThread();
MyThread myThread2=new MyThread();
MyThread myThread3=new MyThread();
myThread1.start();
myThread2.start();
myThread3.start();
}
//因为继承Thread每次都会新创建一个任务,所以可以达到效果,而使用Runnable不能够达到效果
public static class MyThread extends Thread{
int count=0;
@Override
public void run() {
while(count<100){
count++;
Singleton.getSingleton();
}
}
}
}
3.饿汉模式
//饿汉模式
//线程安全,但每次加载类的时候都会实例化,若存在大量的此种单例模式,则会实例化很多无用实例
public class Singleton {
private static Singleton singleton=new Singleton();
private Singleton() {}
public static Singleton getSingleton(){
System.out.println("Singleton");
return singleton;
}
}
4.饿汉模式
//饿汉模式,和第三种一样,只不过换个写法
public class Singleton {
private static Singleton singleton = null;
static {
singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getSingleton() {
return singleton;
}
}
5.静态内部类
//这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):
第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。
因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,
因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
public class Singleton {
public static class SingletonHolder{
private static final SingletonHolder singletonHolder=new SingletonHolder();
private SingletonHolder(){};
public static SingletonHolder getSingletonHolder(){
return singletonHolder;
}
}
}
6.枚举类(推荐)
public enum Singleton { singleton; //此处可以是任何方法 public void out(){ System.out.println(singleton.hashCode()); } }
使用枚举类作为单例的好处有三点:1.天生线程安全(因为枚举类是线程安全的)2.反序列化后不会创建多个实例 3.防反射攻击(因为枚举类是abstract),反编译如下
public abstract class Singleton extends Enum
{
private Singleton(String s, int i)
{
super(s, i);
}
protected abstract void read();
protected abstract void write();
public static Singleton[] values()
{
Singleton asingleton[];
int i;
Singleton asingleton1[];
System.arraycopy(asingleton = ENUM$VALUES, 0, asingleton1 = new Singleton[i = asingleton.length], 0, i);
return asingleton1;
}
public static Singleton valueOf(String s)
{
return (Singleton)Enum.valueOf(singleton/Singleton, s);
}
Singleton(String s, int i, Singleton singleton)
{
this(s, i);
}
public static final Singleton INSTANCE;
private static final Singleton ENUM$VALUES[];
static
{
INSTANCE = new Singleton("INSTANCE", 0) {
protected void read()
{
System.out.println("read");
}
protected void write()
{
System.out.println("write");
}
};
ENUM$VALUES = (new Singleton[] {
INSTANCE
});
}
}
7.双重检查锁定
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
总结
有两个问题需要注意:
1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
对第一个问题修复的办法是:
- private static Class getClass(String classname)
- throws ClassNotFoundException {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- if(classLoader == null)
- classLoader = Singleton.class.getClassLoader();
- return (classLoader.loadClass(classname));
- }
- }
对第二个问题修复的办法是:
- public class Singleton implements java.io.Serializable {
- public static Singleton INSTANCE = new Singleton();
- protected Singleton() {
- }
- private Object readResolve() {
- return INSTANCE;
- }
- }
更多详情可以访问http://cantellow.iteye.com/blog/838473;
在此感谢这篇文章给我的启示。
Singleton的更多相关文章
- 23种设计模式--单例模式-Singleton
一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...
- 设计模式之单例模式(Singleton)
设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...
- PHP设计模式(四)单例模式(Singleton For PHP)
今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...
- The Java Enum: A Singleton Pattern [reproduced]
The singleton pattern restricts the instantiation of a class to one object. In Java, to enforce this ...
- 最适合作为Java基础面试题之Singleton模式
看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...
- 每天一个设计模式-4 单例模式(Singleton)
每天一个设计模式-4 单例模式(Singleton) 1.实际生活的例子 有一天,你的自行车的某个螺丝钉松了,修车铺离你家比较远,而附近的五金店有卖扳手:因此,你决定去五金店买一个扳手,自己把螺丝钉固 ...
- Qt 中使用Singleton模式需小心
在qt中,使用Singleton模式时一定要小心.因为Singleton模式中使用的是静态对象,静态对象是直到程序结束才被释放的,然而,一旦把该静态对象纳入了Qt的父子对象体系,就会导致不明确的行为. ...
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- C#面向对象设计模式纵横谈——2.Singleton 单件(创建型模式)
一:模式分类 从目的来看: 创建型(Creational)模式:负责对象创建. 结构型(Structural)模式:处理类与对象间的组合. 行为型(Behavioral)模式:类与对象交互中的职责分配 ...
- 【白话设计模式四】单例模式(Singleton)
转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...
随机推荐
- 构建基于Chromium的应用程序
chromium是google chrome浏览器所采用的内核,最开始由苹果的webkit发展而出,由于webkit在发展上存在分歧,而google希望在开发上有更大的自由度,2013年google决 ...
- Java 对象引用方式 —— 强引用、软引用、弱引用和虚引用
Java中负责内存回收的是JVM.通过JVM回收内存,我们不需要像使用C语音开发那样操心内存的使用,但是正因为不用操心内存的时候,也会导致在内存回收方面存在不够灵活的问题.为了解决内存操作不灵活的问题 ...
- junit测试,使用classpath和file 加载文件的区别
用junit测试发现一个问题,怎么加载配置文件?一直都出现这样的错误 ERROR: org.springframework.test.context.TestContextManager - Caug ...
- Kafka随笔一
一.KafKa所涉及到的名词概念: 1. Topic:用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. 2. Partition:是Kafka中横向扩展和一切 ...
- ASP.NET Core - ASP.NET Core MVC 的功能划分
概述 大型 Web 应用比小型 Web 应用需要更好的组织.在大型应用中,ASP.NET MVC(和 Core MVC)所用的默认组织结构开始成为你的负累.你可以使用两种简单的技术来更新组织方法并及时 ...
- EF-InvalidOperation系列
1.InvalidOperation强制转换为值类型 'System.Guid' 失败,因为实例化的值为 null.结果类型的泛型参数或查询必须使用可以为 null 的类型 错误信息: The cas ...
- 扩:new and override
昨天有个网友问我继承里面的new和override关键词有啥区别,呃,我们来看个例子就知道了 new ==>隐藏父类同名方法 override==>覆盖 定义一个父类: public c ...
- ASP.NET MVC5 网站开发实践(二) Member区域 - 咨询管理的架构
咨询.留言.投诉等功能是网站应具备的基本功能,可以加强管理员与用户的交流,在上次完成文章部分后,这次开始做Member区域的咨询功能(留言.投诉都是咨询).咨询跟文章非常相似,而且内容更少.更简单. ...
- 掌握 cinder-scheduler 调度逻辑 - 每天5分钟玩转 OpenStack(48)
上一节我们详细讨论了 cinder-api 和 cinder-volume,今天讨论另一个重要的 Cinder 组件 cinder-scheduler. 创建 Volume 时,cinder-sche ...
- php上传功能集后缀名判断和随机命名
form.php <html> <head> <meta http-equiv="content-type" content="text/h ...