1 线程不安全的实现方法

首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到。这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式。但是如果不需要保证线程安全,则这种方式还是不错的,因为所需要的开销比较小。下面是具体的实现代码:

public Class Singleton
{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance()
{
if( instance == null)
instance = new Singleton ();
return instance;
}
}

我们说过这种实现方式不是thread-safe的,那么可以把上面的方法变成线程安全的吗?当然可以,在方法getInstance()上加上synchronized修饰符就可以实现方法的同步了。但是这样系统开销会很大。具体代码如下:

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

每次有线程调用getInstance()方法,都需要同步判断。这显然不是最好的选择,下面将会陆续介绍几种thread-safe的方法。

2 两种lazy loaded thread-safe的单例模式实现方式

1) DCL (double checked locking 实现法)

double checked locking ,顾名思义,就是双检查法,检查实例INSTANCE是否为null或者已经实例化了。下面是具体的实现代码:

 public class DoubleCheckedLockingSingleton{
private volatile DoubleCheckedLockingSingleton INSTANCE; private DoubleCheckedLockingSingleton(){} public DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//double checking Singleton instance
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}

这种方法也很好理解,我们可以看到有两次对instance是否为null的判断:如果第一次判断不为空,则直接返回实例就可以了;如果instance为空,则进入同步代码块再进行null值判断,再选择是否实例化。第一个null判断可以减少系统的开销。在实际项目中做过多线程开发的都应该知道DCL。

2) lazy initialization holder class 模式实现法

下面是这种方法的实现代码:

public class Singleton {
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
/**
* 私有化构造方法
*/
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}

当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

关于延迟初始化(lazy loaded

“除非绝对必要,否则就不要延迟初始化”。延迟初始化是一把双刃剑,它降低了初始化类或者创建实例的开销,却增加了访问被延迟初始化的域的开销,考虑到延迟初始化的域最终需要初始化的开销以及域的访问开销,延迟初始化实际上降低了性能。

3 静态工厂实现法

因为单例是静态的final变量,当类第一次加载到内存中的时候就初始化了,其thread-safe性由JVM来负责保证。值得注意的是这个实现方式不是lazy-loadedd的。   具体实现代码如下:

 public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getSingleton(){
return INSTANCE;
}
}

4 枚举实现单例(Enum Singleton)

枚举单例(Enum Singleton)是实现单例模式的一种新方式,枚举这个特性是在Java5才出现的,在《Effective Java》一书中有介绍这个特性。下面是这种方法的具体实现代码:

public enum Singleton {
INSTANCE("hello") {
public void someMethod() {
// . . .
}
};
private String name;
private void PrintName(){System.out.println(name);}
protected abstract void someMethod();
}

你可以通过Singleton.INSTANCE来访问该单示例变量。默认枚举实例的创建是线程安全的,但是在枚举中的其他任何方法由程序员自己负责。如果你正在使用实例方法,那么你需要确保线程安全(如果它影响到其他对象的状态的话)。传统单例存在的另外一个问题是一旦你实现了序列化接口,那么它们不再保持单例了,但是枚举单例,JVM对序列化有保证。枚举实现单例的好处:有序列化和线程安全的保证,代码简单

JAVA中单例模式的几种实现方式的更多相关文章

  1. Java中单例模式的几种实现

    目录 懒汉式单例 简单版本 synchronized版本 双重检查(Double-Check)版本 volatile 饿汉式单例 实现1 其他实现方式 静态内部类-Effective Java 枚举- ...

  2. Java中String对象两种赋值方式的区别

    本文修改于:https://www.zhihu.com/question/29884421/answer/113785601 前言:在java中,String有两种赋值方式,第一种是通过“字面量”赋值 ...

  3. 细说java中Map的两种迭代方式

    曾经对java中迭代方式总是迷迷糊糊的,今天总算弄懂了.特意的总结了一下.基本是算是理解透彻了. 1.再说Map之前先说下Iterator: Iterator主要用于遍历(即迭代訪问)Collecti ...

  4. Java中Map的4种遍历方式

    第一种方式:这是平常用的最多也最可取的一种遍历方式. for (Map.Entry<String, Object> entry : map.entrySet()) { System.out ...

  5. java中线程的几种实现方式

    1. 继承Thread类来实现 class MyThread extends Thread{ @Override public void run() { System.out.println(&quo ...

  6. XML解析——Java中XML的四种解析方式

    XML是一种通用的数据交换格式,它的平台无关性.语言无关性.系统无关性.给数据集成与交互带来了极大的方便.XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已. XML的解析方式分为四 ...

  7. Java中容器的两种初始化方式比较

    List,Set,Map的两种初始化赋值方式  List List<Integer> list2 = new ArrayList<Integer>(); for (int i= ...

  8. java中线程的三种实现方式

    一下记录下线程的3中实现方式:Thread,Runnable,Callable 不需要返回值时,建议使用Runnable:有返回值时建议使用Callable 代码如下所示: package com.f ...

  9. XML解析——Java中XML的四种解析方式(转载 by 龍清扬)

    XML是一种通用的数据交换格式,它的平台无关性.语言无关性.系统无关性.给数据集成与交互带来了极大的方便.XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已. XML的解析方式分为四 ...

随机推荐

  1. bitbucket和Mercurial安装和相关

    应为工作需要,需要使用bitbucket和Mercurial进行软件开发管理.下面简单介绍以下这些东西和他的安装. bitbucket是一个类似github的软件开发管理工具,和github不同,bi ...

  2. UESTC 886 方老师金币堆 --合并石子DP

    环状合并石子问题. 环状无非是第n个要和第1个相邻.可以复制该行石子到原来那行的右边即可达到目的. 定义:dp[i][j]代表从第i堆合并至第j堆所要消耗的最小体力. 转移方程:dp[i][j]=mi ...

  3. 优化后的二次测试Miller_Rabin素性测试算法

    ll random(ll n) { return (ll)((double)rand()/RAND_MAX*n + 0.5); } ll pow_mod(ll a,ll p,ll n) { ) ; l ...

  4. linux下批量替换文件内容

    1.网络上现成的资料 格式: sed -i "s/查找字段/替换字段/g" `grep 查找字段 -rl 路径` linux sed 批量替换多个文件中的字符串: (此命令很强大) ...

  5. SQL Server进制

    在项目中,大家可能都遇到过,需要把十进制转换为其他进制的情况,google上一搜,已经有很多2进制.8进制.16进制和十进制的转换方法.但是在一些项目中,这些可能无法满足要求,可能需要17.18甚至是 ...

  6. Cobbler自动化批量安装linux服务器的操作记录

    Cobbler为何物?Cobbler是一个快速网络安装linux的服务,而且在经过调整也可以支持网络安装windows.该工具使用python开发,小巧轻便(才15k行python代码),使用简单的命 ...

  7. 06Spring_使用注解配置bean对象

    Spring注解开发需要jar包 和 xml开发 一样的 ! 第一步: 新建项目, 导入jar包(就是前一篇文章里面的那几个核心jar包) 第二步: 在需要spring创建对象类上面 添加@Compo ...

  8. ueditor集成自己的ImageServer时出现错误的原因分析

    1.场景:应用是一个独立的站点,ImageServer是一个独立的站点,因此存在跨域的问题. 2.遇到的详细错误“网络链接错误,请检查配置后重试!” 我使用uploadify测试是没问题的.使用ued ...

  9. javarebel热部署 (转)

    Java web开发部署效率浅析 在进行java web程序开发过程中,经常遇到这种问题,修改一个java文件(*.java),需要重启web服务器(如tomcat,weblogic等),部署项目.而 ...

  10. JS第一个动画

    描述:让页面中的一个盒子平稳向右移动到指定位置 <div id="div1"> <div> JS代码实现 <script type="tex ...