[工作中的设计模式]单例模式singleton
一、模式解析:
单例模式是最简单和最常用的设计模式,面试的时候,不管新毕业的学生还是已经工作多年的筒子,对单例模式基本都能聊上两句。单例模式主要体现在如下方面:
1、类的构造函数私有化,保证外部不能直接使用构造函数创建类的实例
2、提供获取实例的方法,外部可以通过此方法获取已经创建好的实例对象,
3、获取实例的方法必须保证实例唯一性。
4、根据获取实例方法保证唯一性的方式,单例模式又分为以下几种:
二、模式代码
1、懒汉模式
/**
* 单例模式-懒汉模式
* 懒汉模式意味着此模式比较懒惰,直到系统发起调用时候才会创建此对象的实例。
* 需要注意的是要保证线程的同步,防止出现多个实例创建的情况
* @author zjl
*
*/
public class Singleton1 {
//创建元素实例
private static Singleton1 singleton;
//构造对象置为私有变量,不能从外部进行创建
private Singleton1(){ }
/**
* public方法,在外部调用时候,获取此对象的实例
* 注意需要使用同步方法,防止多线程时候产生多实例
* @return 单例对象实例
*/
public static synchronized Singleton1 getInstance(){
if(singleton==null){
singleton=new Singleton1();
}
return singleton;
}
}
懒汉模式实现了对象的懒加载,不过缺点是由于getInstance上加了同步关键字,导致此方法只能有一个线程访问,效率会比较低
2、为了解决懒汉模式的速度问题,引入检测,也就是加锁之前先做一次判定,并将实例声明为volatile
public class Singleton {
private volatile static Singleton instance; //声明成 volatile
private Singleton (){} public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
} }
3、痴汉模式
/**
* 单例模式的痴汉模式
* 痴汉模式表示此模式很急,一旦对象初始化,立马创建一个实例,以后获得实例的都采用此实例
* @author zjl
*
*/
public class Singleton2 {
/**
* 私有构造函数,保证不能从外部创建实例
*/
private Singleton2(){}
//痴汉模式重点,对象初始化直接创建实例
private static Singleton2 singleton=new Singleton2(); /**
* 直接返回之前创建的实例
* @return 对象实例
*/
public static Singleton2 getInstance() {
return singleton;
}
}
痴汉模式不会存在线程同步问题,但是缺点是不是懒加载,对象创建后立马创建实例。
4、静态内部类
/**
* 使用静态内部类来创建单例
* @author zjl
*
*/
public class Singleton3 {
//私有化构造函数,使他不能在外部被创建
private Singleton3(){};
//创建静态内部类,初始化成员变量
private static class SingletonHodler{
private static final Singleton3 INSTANCE=new Singleton3();
}
/**
* 获取实例方法,执行时候才去创建实例
* @return
*/
public static final Singleton3 getInstance(){
return SingletonHodler.INSTANCE;
}
}
静态内部类创建的方法使用不多,也是在面试中很少了解到的,但却是最为推荐的方法,因为他实现了懒加载,线程安全且不需要依赖jdk版本。
三、应用场景
单例模式在框架中应用较多,比如spring的bean管理可以设置是否为单例模式,数据库对象实例化
四、场景代码
由于比较简单,略过。
五、疑问解决
为什么我们在做懒汉模式的双重检测的时候,需要将对象改变为使用volatile进行修饰,此处与java的内存模式有关,并涉及到synchronized和volatile的内存操作
1、java的内存模型主要分为主内存和工作内存,对应计算机的结构可以认为是计算机内存和cpu的高速缓存,对于任何主内存的数据进行操作时候,必须先将数据读取到工作内存形成一个备份,当对工作内存的数据操作完成后,将工作内存数据重新同步到主内存。
对于工作内存和主内存的操作主要为:读取过程-read,load,use,写入过程 assign、store、write,这六个操作均具有原子性,但整个流程不是原子性。因此为了保证可见性和原子性,增加了lock和unlock操作,主要针对主内存区数据的锁定,一旦主内存区数据被lock,表示此线程独占了变量,不可被其他线程更改
2、synchronized的作用主要有两点:程序临界区和内存同步。
程序临界区:是一段仅允许一个线程进行访问的程序,一旦一个线程进入临界区,另外线程进入临界区只能在临界区外进行等待,等临界区线程执行完毕后,其他线程开始争夺资源,胜利者进入临界区,其他线程继续等待。
内存同步:主要是主内存和工作内存的同步。进入临界区时,如果有工作内存的数据未被同步的主内存,则先进行同步,成为其他线程可见状态。工作内存的数据会被丢失,如果要使用,需要重新进行read和load操作。退出临界区时,将工作内存数据写入主内存,保证修改可见。
3、volatile的作用是保证数据可见性,对于任何工作内存的assign操作会立刻store和write到主内存中,同时使其他工作内存放弃原有持有数据。但是volatile不保证操作原子性。
4、具体分析可能出现的问题:
public class Singleton {
private static Singleton instance; //声明成 volatile
private Date date=new Date;
private Singleton (){} public static Singleton getSingleton() { //
if (instance == null) { //
synchronized (Singleton.class) { //
if (instance == null) { //
instance = new Singleton(); //
}
} //6
}
return instance; //
}
public Date getDate(){
return date;
} }
如上边的代码,我们将原有懒汉模式稍作修改,增加了date字段,模拟双线程A与B的创建过程
由于new Singleton()不是一个原子操作,程序在进入和出临界区时候,均会同步主内存的内容,除此之外B线程如果在3和6之间调用,就会发生A线程的工作线程内的内容不一定全部写入了主内存,假设此阶段instance写入了主内存,但是date没有写入,B线程将对data内容不可见,因此getDate将返回null。
如果对instance添加了volatile,那么针对instance的修改,随时b线程都是可见的。
[工作中的设计模式]单例模式singleton的更多相关文章
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
- 设计模式 单例模式(Singleton) [ 转载 ]
设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- .net中的设计模式---单例模式
.net设计模式: Net设计模式实例之单例模式( Singleton Pattern) 一 : 单例模式的简介:(Brief Introduction) 单例模式(Singleton Patter ...
- [工作中的设计模式]享元模式模式FlyWeight
一.模式解析 Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是因为这样更能反映模式的用意.享元模式是对象的结构模式.享元模式以共享的方式高效地支持 ...
- [工作中的设计模式]原型模式prototype
一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.pro ...
- .net中的设计模式---单例模式,涉及lock的用法
.客户端代码 static void Main(string[] args) { Singleton singleton2 = Singleton.GetInstance(); Singleton s ...
- [工作中的设计模式]解释器模式模式Interpreter
一.模式解析 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 以上是解释器模式的类图,事实上我 ...
随机推荐
- POJ 1830 开关问题 (高斯消元)
题目链接 题意:中文题,和上篇博客POJ 1222是一类题. 题解:如果有解,解的个数便是2^(自由变元个数),因为每个变元都有两种选择. 代码: #include <iostream> ...
- .NET微信公众号开发-4.0公众号消息处理
一.前言 微信公众平台的消息处理还是比较完善的,有最基本的文本消息,到图文消息,到图片消息,语音消息,视频消息,音乐消息其基本原理都是一样的,只不过所post的xml数据有所差别,在处理消息之前,我们 ...
- php curl get post
post有3种. 1.post方式 privatefunction send_post($url,$post_data){ $ch = curl_init($url); curl_setopt($ch ...
- Redis自定义动态字符串(sds)模块(一)
Redis开发者在开发过程中没有使用系统的原始字符串,而是使用了自定义的sds字符串,这个模块的编写是在文件:sds.h和sds.c文件中.Redis自定义的这个字符串好像也不是很复杂,远不像ngin ...
- chaper3_exerise_Uva1568_Molar_Mass_分子量
#include<iostream> #include<iomanip> #include<string> #include<cctype> using ...
- Codeforces Round #370 (Div. 2)(简单逻辑,比较水)
C. Memory and De-Evolution time limit per test 2 seconds memory limit per test 256 megabytes input s ...
- Swift - 键盘弹出样式
Swift提供了11种键盘类型: 在开发中,我们可以根据不同的需求,选择不同的键盘样式,例如,当我们只需要输入手机号码时,可以选择纯数字类型的键盘(.NumbersAndPunctuation),当我 ...
- codevs 1488GangGang的烦恼
题目链接:http://codevs.cn/problem/1488/ 写个高精度大数运算就行 #include<cstdio> #include<iostream> #inc ...
- SpringBoot Jms
https://dzone.com/articles/spring-boot-example-of-spring-integration-and-acti
- 攻城狮在路上(壹) Hibernate(八)--- 映射Hibernate组成关系
一.使用组成关系的原则: 在不导致数据冗余的前提下,尽可能减少数据库表的数目及表之间的外键参照关系,因为建立多个表的连接是很耗时的操作. 举例说明:Customer类中的Address属性,可以通过组 ...