Java中单例设计模式总结
两种单例常见的实现方式:
1:懒汉的设计模式,在第一次调用的时候才完成相关的初始化操作
懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间
package com.xiaohao.test;
/**
* 使用懒汉模式创建一个单例模式
* @author lenovo
*
*/
public class Singleton {
private static Singleton instance=null;
/**
* 返回或者创建相关的单例实例
* @return
*/
public static synchronized Singleton getInstance(){//使用同步方法保证线程安全
if(instance==null){
    instance=new Singleton();
}
return instance;
}
/**
* 一个私有的构造方法,使外部的对象不能new相关的实例,这个尤其要注意,一定要提供默认的私有化的方法去覆盖默认的构造方法
* 否则的话,如果用户直接去new一个对象的话,就无法保证单例了~~~
*/
private Singleton(){}
}
2:饿汉的设计模式,在初始化类的过程中就会完成相关实例的初始化,一般认为这种方式要更加安全些
饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
package com.xiaohao.test;
/**
 * 使用饿汉模式创建一个单例 模式
 * @author lenovo
 *
 */
public class Singleton {
//这个内部的单例仅仅用于内部访问,在初始化相关类的时候,就完成了相关单例类的初始化
    //同时使用final修饰符修饰,一旦初始化值之后,不允许修改相关的值,这样也就实现了系统中的唯一
    private static final Singleton instance=new Singleton();
/**
     * 返回或者创建相关的单例实例
     * @return
     */
    public static Singleton getInstance(){
    	return instance;
    }
/**
* 一个私有的构造方法,使外部的对象不能new相关的实例,这个尤其要注意,一定要提供默认的私有化的方法去覆盖默认的构造方法
* 否则的话,如果用户直接去new一个对象的话,就无法保证单例了~~~
*/
private Singleton(){}
}
此外还有一个比较给力的实现单例的方式,推荐大家使用这种方式:
/**
 *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
 *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
 */
static class SingletonHolder {
/**
 * 静态初始化器,由JVM来保证线程安全
 */
static Singleton instance = new Singleton();    
  }    
   
  public static Singleton getInstance() {    
    return SingletonHolder.instance;    
  }    
   
} 
  
在加载singleton时并不加载它的内部类SingletonHolder,而在调用getInstance()时调用SingletonHolder时才加载SingletonHolder,从而调用singleton的构造函数,实例化singleton,从而达到lazy loading的效果。
3:双从锁的设计模式,实现在多线程下,相关性能的提高,尤其避免懒汉模式中,每次获取实例的时候都需要线程同步的问题
双重锁的主要目的是解决每次获取实例的时候都需要进行线程等待,只有在第一次实例化时,才启用同步机制,提高了性能。
提示:由于volatile关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检 查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。
package com.xiaohao.test;
/**
* 使用双重锁模式创建一个单例模式
* @author lenovo
*
*/
public class Singleton {
private static Singleton instance=null;
/**
* 返回或者创建相关的单例实例
* @return
*/
public static Singleton getInstance(){
if(instance==null)
{
 synchronized (Singleton.class){//需要注意的是,这里锁住的是类,一旦加上这个类锁之后,所有和这个类相关的操作(包括这个类的所有属性和方法)都需要进行相关的等待
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
/**
* 一个私有的构造方法,使外部的对象不能new相关的实例,这个尤其要注意,一定要提供默认的私有化的方法去覆盖默认的构造方法
* 否则的话,如果用户直接去new一个对象的话,就无法保证单例了~~~
*/
private Singleton(){}
}
}
这个方法表面上看起来很完美,你只需要付出一次同步块的开销,但它依然有问题。除非你声明instance变量时使用了volatile关键字。没有volatile修饰符,可能出现Java中的另一个线程看到个初始化了一半的instance的情况,但使用了volatile变量后,就能保证先行发生关系(happens-before relationship)。对于volatile变量_instance,所有的写(write)都将先行发生于读(read),在Java 5之前不是这样,所以在这之前使用双重检查锁有问题。现在,有了先行发生的保障(happens-before guarantee),你可以安全地假设其会工作良好。另外,这不是创建线程安全的单例模式的最好方法,你可以使用枚举实现单例模式,这种方法在实例创建时提供了内置的线程安全。另一种方法是使用静态持有者模式(static holder pattern),这种方式就是开始的饿汉模式,所以说饿汉模式相对来说比较安全一些。
package com.xiaohao.test;
/**
* 使用双重锁模式创建一个单例模式,双重锁模式下的进一步优化
* @author lenovo
*
*/
public class Singleton {
	//注意这里面的volatile 这个关键字,它的主要作用是写(write)优于读(read),前提是jdk的版本要不低于jdk5.0
private volatile static Singleton instance=null;
/**
* 返回或者创建相关的单例实例
* @return
*/
public static Singleton getInstance(){
if(instance==null)
{
 synchronized (Singleton.class){//需要注意的是,这里锁住的是类,一旦加上这个类锁之后,所有和这个类相关的操作(包括这个类的所有属性和方法)都需要进行相关的等待
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
/**
* 一个私有的构造方法,使外部的对象不能new相关的实例,这个尤其要注意,一定要提供默认的私有化的方法去覆盖默认的构造方法
* 否则的话,如果用户直接去new一个对象的话,就无法保证单例了~~~
*/
private Singleton(){}
}
4:使用枚举实现单例模式,这种方法在实例创建时提供了内置的线程安全
package com.xiaohao.test;
/**
* 使枚举的方法创建一个单例类
* @author lenovo
*
*/
	public enum Singleton {
	    INSTANCE;// 使用枚举类的方法创建一个单例,据说这是目前位置,无论是多线程还是,防止反射创建
	                     //相关对象最有用的方法,推荐使用
	}
单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。使用enum关键字来实现单例模式的好处是这样非常简洁,并且无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。——来自《Effective Java》
***知识天补充(拷贝的)----针对谷歌那个工程师的方法:
这个模式综合使用了Java的类级内部类和多线程缺省同步锁的知识,很巧妙地同时实现了延迟加载和线程安全。
1.相应的基础知识
- 什么是类级内部类?
 
简单点说,类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类。
类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。
类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
- 多线程缺省同步锁的知识
 
大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
2.访问final字段时
3.在创建线程之前创建对象时
4.线程可以看见它将要处理的对象时
2.解决方案的思路
要想很简单地实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。比如前面的饿汉式实现方式。但是这样一来,不是会浪费一定的空间吗?因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。
如果现在有一种方法能够让类装载的时候不去初始化对象,那不就解决问题了?一种可行的方式就是采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象实例,从而同时实现延迟加载和线程安全。
Java中单例设计模式总结的更多相关文章
- Java中单例设计模式,饿汉式和懒汉式
		
Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯 ...
 - java中单例设计模式
		
在java中创建单例的方式主要有三种:饿汉式.懒汉式.登记式.以下内容均是摘抄自 http://blog.csdn.net/jason0539/article/details/23297037/ 一. ...
 - Java单例设计模式的实现
		
1. 单例设计模式的定义 单例设计模式确保类只有一个实例对象,类本身负责创建自己的对象并向整个系统提供这个实例.在访问这个对象的时候,访问者可以直接获取到这个唯一对象而不必由访问者进行实例化. 单例设 ...
 - Java中单例
		
Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例.饿汉式单例 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3. ...
 - Java——单例设计模式
		
设计模式:解决某一类问题最行之有效的方法.Java中23种设计模式:单例设计模式:解决一个类在内存中只存在一个对象. 想要保证对象唯一.1,为了避免其他程序过多建立该类对象.先禁止其他程序建立该类对象 ...
 - 转:java单例设计模式
		
本文转自:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html 单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton ...
 - Java单例设计模式和多例设计模式
		
单例设计模型 教学视频链接:https://edu.aliyun.com/course/1011 1,private不可以在类外部访问,但可以在内部访问 2,此时Singleton类内部的instan ...
 - java 单例设计模式
		
1.饿汉单例设计模式: 步骤 : 1.定义一个私有的静态成员变量来引用对象(私有的静态对象),设置对象唯一. 2.私有化构造方法,防止new对象. 3.创建一个公开的静态方法,返回上面的 ...
 - java单例设计模式总结及举例
		
* 设计模式:前人总结出来的经验,被后人直接拿来使用. * 单例设计模式:一个类只允许有一个对象,将这个对象作为一个全局的访问点,提供出去供大家使用. * 分析: * 1.用户只能有一个对象 * 2. ...
 
随机推荐
- Windows环境自动获取AWR报告
			
1.双击awr.cmd,通过cmd窗口运行awr.sql cmd.exe /c sqlplus lcam_1230/zcpzg1z_1230@54_orcl @awr.sql awr.cmd 2.aw ...
 - Android-onTouchEvent方法的使用
			
手机屏幕事件的处理方法onTouchEvent.该方法在View类中的定义,并且所有的View子类全部重写了该方法,应用程序可以通过该方法处理手机屏幕的触摸事件.该方法的签名如下所示. public ...
 - 图片的android:src 及android:background共存
			
---恢复内容开始--- 需求:给ImageView添加背景色 效果: 实现分析: 1.目录结构: 代码实现: 1.activity_main.xml <merge xmlns:android= ...
 - wpa_cli调试工具的使用
			
1: run wpa_supplicant first use the following command: wpa_supplicant -Dwext -iwlan0 -C/data/system/ ...
 - 两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]…*a[N-1]/a[i];
			
转自:http://blog.csdn.net/shandianling/article/details/8785269 问题描述:两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i ...
 - FOJ2022车站   线段树区间合并
			
http://acm.fzu.edu.cn/problem.php?pid=2022 刚开始MLE,用map对应,果断爆内存了,然后改用去重,离散化, lowbound查找元素位置,速度还不错,不过p ...
 - 仿酷狗音乐播放器开发日志十九——CTreeNodeUI的bug修复二(附源码)
			
转载请说明原出处,谢谢 今天本来打算把仿酷狗播放列表的子控件拖动插入功能做一下,但是仔细使用播放列表控件时发现了几个逻辑错误,由于我的播放 列表控件是基于CTreeViewUI和CTreeNodeUI ...
 - hive 传递变量的两种方式
			
在使用hive开发数据分析代码时,经常会遇到需要改变运行参数的情况,比如select语句中对日期字段值的设定,可能不同时间想要看不同日期的数据,这就需要能动态改变日期的值.如果开发量较大.参数多的话, ...
 - Dictionary<实体,List<实体>>的比较
			
当Dictionary中Key为实体时,进行用ContainsKey比较会发现,就算Model为一样但是结果比较为不存在: 故用以下代码即可,现将Keys转换ToArray(),再用数组的Contai ...
 - [转] Web前端优化之 Cookie篇
			
原文链接: http://lunax.info/archives/3095.html Web 前端优化最佳实践第三部分面向 Cookie .目前只有 2 条实践规则. 1. 缩小 Cookie (Re ...