单例模式总结(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的反射机制创建相关的对象,即使使用private 重写了相关的构造方法~~
单例模式总结(Java版)的更多相关文章
- 你真的会写单例模式吗-------Java实现
转载: 你真的会写单例模式吗--Java实现 单例模式可能是代码最少的模式了,但是少不一定意味着简单,想要用好.用对单例模式,还真得费一番脑筋.本文对Java中常见的单例模式写法做了一个总结,如有错漏 ...
- 网页爬虫的设计与实现(Java版)
网页爬虫的设计与实现(Java版) 最近为了练手而且对网页爬虫也挺感兴趣,决定自己写一个网页爬虫程序. 首先看看爬虫都应该有哪些功能. 内容来自(http://www.ibm.com/deve ...
- [转] 你真的会写单例模式吗——Java实现
你真的会写单例模式吗——Java实现 原文:http://www.tuicool.com/articles/MBrUfy6 单例模式可能是代码最少的模式了,但是少不一定意味着简单,想要用好.用对单例模 ...
- pureMVC java版搭建流程
转自:http://blog.csdn.net/sutaizi/article/details/6588004 pureMVC 是一个轻量级的框架 它在 flex中非常流行(和cairngorm差不多 ...
- ArcGIS Server 10 Java 版的Rest服务手动配置方法
Java版的Manager中发布的服务默认只发布了该服务的SOAP接口,而REST接口需要用户在信息服务器,如Tomcat. Apache.WebLogic等中手工配置.由于在Java版的Server ...
- PetaPojo —— JAVA版的PetaPoco
背景 由于工作的一些原因,需要从C#转成JAVA.之前PetaPoco用得真是非常舒服,在学习JAVA的过程中熟悉了一下JAVA的数据组件: MyBatis 非常流行,代码生成也很成熟,性能也很好.但 ...
- 【转载】java版打字练习软件
网上找到一个java版的打字的游戏 import java.applet.Applet; import java.applet.AudioClip; import java.awt.Dimension ...
- 微博地址url(id)与mid的相互转换 Java版
原理: 新浪微博的URL都是如:http://weibo.com/2480531040/z8ElgBLeQ这样三部分. 第一部分(绿色部分)为新浪微博的域名,第二部分(红色部分)为博主Uid,第三部分 ...
- JCEF3——谷歌浏览器内核Java版实现(一):使用jawt获取窗体句柄
前言 最近一段时间研究谷歌浏览器内核.谷歌浏览器内核一直开源,并维护更新,它的开源项目中内核更新速度和Chrome浏览器版本更新进度一样!而且它不同于WebKit(值得一题的是谷歌浏览器已不使用Web ...
- Selenium关键字驱动测试框架Demo(Java版)
Selenium关键字驱动测试框架Demo(Java版)http://www.docin.com/p-803493675.html
随机推荐
- OLAP、OLTP的介绍和比较 via csdn
OLAP.OLTP的介绍和比较 数据处理大致可以分成两大类: OLTP(On-Line Transaction Processing)联机事务处理 也称为面向交易的处理系统,其基本特征是顾客的原始数据 ...
- wireshark tcp 协议分析 z
虽然知道wireshark是抓包神器,只会大概大概用一下,还用一下下tcpdump,略懂一点BPF过滤器,也知道一点怎么用 wirkshark过滤相关的报文,但是对于详细的字段的含义,如何查看TCP的 ...
- sqlserver 中 lastindexof 功能
create table tb(imgPath varchar(50)) insert into tb select 'd1/d2/f1'--d1/d2/dd/f1 select left(i ...
- SqlServer获取两个日期时间差
SELECT datediff(yy,'2010-06-1 10:10',GETDATE()) --计算多少年 SELECT datediff(q,'2011-01-1 10:10',GETDATE( ...
- bzoj 1324 Exca王者之剑(黑白染色,最小割)
[题意] 两相邻点不能同时选,选一个点集使得权值和最大. 出题人语文好... [思路] 将图进行黑白二染色,然后构建最小割模型. [代码] #include<set> #include&l ...
- fedora20安装spin以及用户界面ispin
(博客园-番茄酱原创) (最近感觉用make会出现库错误,所以改进了教程,把之前的make步骤省掉了,直接下载可执行文件进行配置最简单啦...) 1.首先,下载对应版本的spin,我64位的fedor ...
- mac搭建PHP开发环境
在Mac系统上搭建Php服务器环境: LAMP: Linux Apache MySQL PHP MAMP: MACOS APACHE(自带) MYSQL(需自己安装) PHP(自带) 一.APACHE ...
- Spring For Android初体验
Spring For Android是Spring框架的一个扩展,其主要目的在乎简化Android本地应用的开发,这其中包括了你可以使用该项目提供的 RestTemplate来为你的Android客户 ...
- 对JDK,JRE,JVM的理解
JAVA用到现在还是分不太清楚JDK,JRE,JVM这三者的区别与联系,一直都是模模糊糊的.所以今天整理下此中的关系. 简单说明:我们编写的.java文件经过JDK(JDK的bin目录下javac.e ...
- Xcode 的正确打开方式——Debugging
程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能够大幅提升代码调试效率的方式. “If debuggi ...