一. 什么是单例模式

因程序需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

二. 单例模式的特点

1. 单例模式只能有一个实例。

2. 单例类必须创建自己的唯一实例。

3. 单例类必须向其他对象提供这一实例。

三. 单例模式VS静态类

在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。

1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。

2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。

3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。

4. 基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。

5. 静态方法有更高的访问效率。

6. 单例模式很容易被测试。

几个关于静态类的误解:

误解一:静态方法常驻内存而实例方法不是。

实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。

误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。

实际上,都是加载到特殊的不可写的代码内存区域中。

静态类和单例模式情景的选择:

情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。

情景二:需要维持一些特定的状态,此时更适合使用单例模式。

四. 单例模式的实现

1. 懒汉模式

public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){ }
public static SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}

如上,通过提供一个静态的对象instance,利用private权限的构造方法和getInstance()方法来给予访问者一个单例。

缺点是,没有考虑到线程安全,可能存在多个访问者同时访问,并同时构造了多个对象的问题。之所以叫做懒汉模式,主要是因为此种方法可以非常明显的lazy loading。

针对懒汉模式线程不安全的问题,我们自然想到了,在getInstance()方法前加锁,于是就有了第二种实现。

2. 线程安全的懒汉模式

public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){ }
public static synchronized SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}

然而并发其实是一种特殊情况,大多时候这个锁占用的额外资源都浪费了,这种打补丁方式写出来的结构效率很低。

3. 饿汉模式

public class SingletonDemo {
private static SingletonDemo instance=new SingletonDemo();
private SingletonDemo(){ }
public static SingletonDemo getInstance(){
return instance;
}
}

直接在运行这个类的时候进行一次loading,之后直接访问。显然,这种方法没有起到lazy loading的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。

4. 静态类内部加载

public class SingletonDemo {
private static class SingletonHolder{
private static SingletonDemo instance=new SingletonDemo();
}
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
return SingletonHolder.instance;
}
}

使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的。

5. 枚举方法

enum SingletonDemo{
INSTANCE;
public void otherMethods(){
System.out.println("Something");
}
}

 

Effective Java作者Josh Bloch 提倡的方式,在我看来简直是来自神的写法。解决了以下三个问题:

(1)自由序列化。

(2)保证只有一个实例。

(3)线程安全。

如果我们想调用它的方法时,仅需要以下操作:

public class Hello {
public static void main(String[] args){
SingletonDemo.INSTANCE.otherMethods();
}
}

这种充满美感的代码真的已经终结了其他一切实现方法了。

6. 双重校验锁法

public class SingletonDemo {
private volatile static SingletonDemo instance;
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
}

接下来我解释一下在并发时,双重校验锁法会有怎样的情景:

STEP 1. 线程A访问getInstance()方法,因为单例还没有实例化,所以进入了锁定块。

STEP 2. 线程B访问getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程1锁定。

STEP 3. 线程A进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。

STEP 4. 线程B进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。

STEP 5. 线程A获取到了单例实例并返回,线程B没有获取到单例并返回Null。

理论上双重校验锁法是线程安全的,并且,这种方法实现了lazyloading。

Java公开课-06.单例的更多相关文章

  1. 学习经验分享(最近听了一节Java公开课)

    最近听了一节Java公开课,讲的Tomcat8.0的,老师分享的学习方法很好, 时间和精力要用对地方 1.学习一个知识的广度和深度,先学主要的主流的,不要学了很多不该学,没必要学的东西 2.要花时间总 ...

  2. Java学习笔记之---单例模型

    Java学习笔记之---单例模型 单例模型分为:饿汉式,懒汉式 (一)要点 1.某个类只能有一个实例 2.必须自行创建实例 3.必须自行向整个系统提供这个实例 (二)实现 1.只提供私有的构造方法 2 ...

  3. java中安全的单例与不安全的单例

    java中安全的单例与不安全的单例 1.内部静态类(安全的) public class Singleton { private static class SingletonHolder{ privat ...

  4. JAVA的设计模式之单例设计模式

    1.确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例. 1)理论 Java Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 使用Singl ...

  5. java基础学习之单例设计模式学习

    最近狂补java基础的我重新学习了下单例,下面直接贴出代码,以作备忘 package com.darling.single; /** * 单例模式 * 单例即在内存中只存在该类的一个实例,要想实现这个 ...

  6. 【java设计模式】之 单例(Singleton)模式

    1. 单例模式的定义 单例模式(Singleton Pattern)是一个比較简单的模式.其原始定义例如以下:Ensure a class has only one instance, and pro ...

  7. Java设计模式:Singleton(单例)模式

    概念定义 Singleton(单例)模式是指在程序运行期间, 某些类只实例化一次,创建一个全局唯一对象.因此,单例类只能有一个实例,且必须自己创建自己的这个唯一实例,并对外提供访问该实例的方式. 单例 ...

  8. Java中Class和单例类的作用与类成员的理解

    Java中Class类的作用与深入理解 在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识.这个信息跟踪着每个对象所属的类.JVM利用运行时信息选择相应的方法执行.而保存 ...

  9. java新手笔记12 单例

    1.单例 package com.yfs.javase; public class Singleton { //private static final Singleton single = new ...

随机推荐

  1. 套接字编程相关函数(1:套接字地址结构、字节序转换、IP地址转换)

    1. 套接字地址结构 1.1 IPv4套接字地址结构 IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中.下 ...

  2. Gradle 1.12用户指南翻译——第二十三章. Java 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  3. FPGrowth 实现

    在关联规则挖掘领域最经典的算法法是Apriori,其致命的缺点是需要多次扫描事务数据库.于是人们提出了各种裁剪(prune)数据集的方法以减少I/O开支,韩嘉炜老师的FP-Tree算法就是其中非常高效 ...

  4. 在app内打开自己app的专用设置界面

    在我们的APP中,可能会使用多种服务,例如定位.推送.相册.拍照.通讯录等.选择是否允许一般只出现在安装app后第一次打开时,可是我们依然需要在使用到某种服务的时候判断是否用户是否允许了该服务,因为用 ...

  5. 网站开发进阶(十八)js获取html标签中的值

    js获取html标签中的值 项目开发过程中,由于需求所迫,需要获取html标签元素中的内容,下面做一简单总结.以下所讲的示例适用于其它标签元素. 主要包括2中方法获取元素内容: 方法一:.innerT ...

  6. Hibernate学习大全

    第1课 课程内容. 6 第2课Hibernate UML图. 6 第3课 风格. 7 第4课 资源. 7 第5课 环境准备. 7 第6课 第一个示例HibernateHelloWorld 7 第7课 ...

  7. android JNI调用机制

    JNI的出现使得开发者既可以利用Java语言跨平台.类库丰 富.开发便捷等特点,又可以利用Native语言的高效. JNI是JVM实现中的一部分,因此Native语言和Java代码都运行在JVM的宿主 ...

  8. 软件工程师 Book

    一.软件工程师  --Clean Code<代码整洁之道>  --Implementation Patterns<实现模式>  --Code Complete<代码大全& ...

  9. Boyer-Moore算法

    1.概述 在用于查找子字符串的算法当中,BM(Boyer-Moore)算法是目前相当有效又容易理解的一种,一般情况下,比KMP算法快3-5倍. BM算法在移动模式串的时候是从左到右,而进行比较的时候是 ...

  10. Zeromq自连接错误

    Zeromq自连接错误(金庆的专栏)Zeromq消息中间件开发的服务器和客户端不必按顺序启动,客户端可以在服务器开启之前启动.这是Zmq特别好用的一大特性.利用该特性,网游各功能服务器可以任意重启,实 ...