单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。Java里面实现的单例是一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的 ClassLoad 装载实现单例类的时候就会创建一个类的实例。在 Java语言中,这样的行为能带来两大好处:

  1. 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;

  2. 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。

因此对于系统的关键组件和被频繁使用的对象,使用单例模式可以有效地改善系统的性能。单例模式的核心在于通过一个接口返回唯一的对象实例。首要的问题就是要把创建实例的权限收回来,让类自身来负责自己类的实例的创建工作,然后由这个类来提供外部可以访问这个类实例的方法

单例模式基本实现

/**
* 单例模式基本实现
*/
public class Singleton { // 首先单例模式必须要有一个private访问级别的构造函数,才能确保单例不会在系统中其他代码内被实例化,其次,instance 成员变量和
// getInstance 方法必须是 static 的。
private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() {
return instance;
}
}

上述代码唯一的不足是无法对 instance 实例做延时加载,例如单例的创建过程很慢,而由于 instance 成员变量是 static 定义的,因此在 JVM加载单例类时,单例对象就会被建立,如果此时这个单例类在系统中还扮演其他角色,那么在任何使用这个单例类的地方都会初始化这个单例变量,而不管是否会被用到。

单例模式实验

public class Singleton {
private Singleton() {
System.out.println("Singleton is create");
} private static Singleton instance = new Singleton(); public static Singleton getInsatnce() {
return instance;
}
public static void createString() {
System.out.println("createString in Singleton");
}
public static void main(String[] args) {
Singleton.createString();
}
}

上述代码运行后的输出如

Singleton is create
createString in Singleton

可以看到,虽然此时并没有使用单例类,但它还是被创建出来,为了解决这类问题,需要引入延迟加载机制。

//延迟加载的单例模式
public class LazySingleton {
private LazySingleton() {
System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null; public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
public static void createString() {
System.out.println("create String");
} public static void main(String[] args) {
LazySingleton.createString();
}
}

上述代码运行后的输出如

create String

上述代码首先对于静态成员变量 instance 初始化赋值 null,确保系统启动时没有额外的负载;其次,在 getInstance()工厂方法中,判断当前单例是否已经存在,若存在则返回,不存在则再建立单例。这里尤其要注意的是,getInstance() 方法必须是同步的,否则在多线程环境下,当线程1正新建单例时,完成赋值操作前,线程2可能判断instance为null,故线程2也将启动新建单例的程序,而导致多个实例被创建,故同步关键字是必须的。由于引入了同步关键字,导致多线程环境下耗时明显增加,两者测试代码如下

非同步的单例模式代码

public class Singleton implements Runnable{
private Singleton() {
System.out.println("Singleton is create");
} private static Singleton instance = new Singleton(); public static Singleton getInsatnce() {
return instance;
}
public static void createString() {
System.out.println("createString in Singleton");
}
public static void main(String[] args) {
//Singleton.createString();
for (int i = 0; i < 5; i++) {
new Thread(new Singleton()).start();
}
}
@Override
public void run() {
long beginTime=System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
Singleton.getInsatnce();
}
System.out.println(System.currentTimeMillis()-beginTime);
}
}

代码运行后输出

Singleton is create
Singleton is create
Singleton is create
Singleton is create
Singleton is create
Singleton is create
1
0
1
2
0

完整的延迟加载方式代码

public class LazySingleton implements Runnable{
private LazySingleton() {
System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null; public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
public static void createString() {
System.out.println("create String");
} public static void main(String[] args) {
//LazySingleton.createString();
for (int i = 0; i < 5; i++) {
new Thread(new LazySingleton()).start();
}
}
@Override
public void run() {
long beginTime=System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
LazySingleton.getInstance();
}
System.out.println(System.currentTimeMillis()-beginTime);
}
}

代码运行后输出

LazySingleton is create
LazySingleton is create
LazySingleton is create
LazySingleton is create
LazySingleton is create
LazySingleton is create
5125
5261
5270
5440
5454

为了解决同步关键字降低系统性能的缺陷,做了一定改进

//延迟加载的单例模式
public class LazySingleton implements Runnable{
private LazySingleton() {
System.out.println("LazySingleton is create");
}
private static LazySingleton instance = null; public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
public static void createString() {
System.out.println("create String");
} public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new LazySingleton()).start();
}
}
@Override
public void run() {
long beginTime=System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
LazySingleton.getInstance();
}
System.out.println(System.currentTimeMillis()-beginTime);
}
}

首先判断instance是不是为null,如果为null,加锁初始化;如果不为null,直接返回instance。

/**
* 解决同步关键字低效率
*
*/
public class StaticSingleton { private StaticSingleton() {
System.out.println("StaticSingleton is create");
} private static class SingletonHolder {
private static StaticSingleton instance = new StaticSingleton();
} public static StaticSingleton getInstance() {
return SingletonHolder.instance;
} }

单例模式使用内部类来维护单例的实例,当 StaticSingleton 被加载时,其内部类并不会被初始化,故可以确保当 StaticSingleton 类被载入 JVM 时,不会初始化单例类,而当 getInstance() 方法调用时,才会加载 SingletonHolder,从而初始化instance。同时,由于实例的建立是时在类加载时完成,故天生对多线程友好,getInstance() 方法也无需使用同步关键字。

登记式单例

  • //类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {

    private static Map<String, Object> map = new HashMap<String, Object>();
static {
/*Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);*/
} // 保护的默认构造子
protected Singleton3() {
} // 静态工厂方法,返还此类惟一的实例
public static Object getInstance(String name) {
if (name == null) {
name = Singleton3.class.getName();
System.out.println("name == null" + "--->name=" + name);
}
if (map.get(name) == null) {
try {
map.put(name, Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
} public static void main(String[] args) {
Singleton3 single3 =(Singleton3) Singleton3.getInstance("demo.pattern.singleton.Singleton3");
Singleton3 single2 =(Singleton3) Singleton3.getInstance("demo.pattern.singleton.Singleton3");
System.out.println(single2);
System.out.println(single3);
} }

登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

参考资料

http://www.ibm.com/developerworks/cn/java/j-lo-Singleton/


Singleton Pattern单例模式的更多相关文章

  1. Python Singleton Pattern(单例模式)

    简介 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. 当 ...

  2. Singleton Pattern(单例模式)

    1.简介 单例模式,顾名思义,即在整个系统中,类的实例对象只有一个. 单例模式具有以下特点: 单例类只能有一个实例 单例类必须自己创建自己的唯一实例 单例类必须给所有其他对象提供这一实例 2.实现 其 ...

  3. Java中的单例模式(Singleton Pattern in Java)

    Introduction 对于系统中的某个类来说,只有一个实例是很重要的,比如只有一个timer和ID Producer.又比如在服务器程序中,配置信息保留在一个文件中,这些配置信息由一个单例对象统一 ...

  4. Singleton Pattern -- 不一样的单例模式

    Singleton Pattern -- 单例模式 单例模式是用来创建一个只能又一个实例的对象. 单例模式类图如下. 单例模式有两大好处: (1)对于频繁使用的对象,可以省略创建对象所话费的时间,这对 ...

  5. Net设计模式实例之单例模式( Singleton Pattern)

    一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...

  6. 深入浅出设计模式——单例模式(Singleton Pattern)

    模式动机对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务:一个系统只能有一个窗口管理器或文件系统:一个系统只能有一个计时工具或ID(序号) ...

  7. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

  8. 设计模式之单例模式(Singleton Pattern)

    单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...

  9. 深入设计模式(二)——单例模式(Singleton Pattern)

    一.单例模式介绍 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯一实例,它就可以严格地控制客户怎样访问它 ...

随机推荐

  1. 工作中积累整理-CSS样式表(一)

    [layout] clear:该属性的值指出了不允许有浮动对象的边. 默认值:none none: 允许两边都可以有浮动对象 both: 不允许有浮动对象 left: 不允许左边有浮动对象 right ...

  2. HDU 1272 小希的迷宫 并查集

    小希的迷宫 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  3. python学习之深入

    一.迭代器和生成器 1.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退. ...

  4. python 给定n,返回n以内的斐波那契数列

    方式一:函数 def fabs(n): a, b = 0, 1 while b < n: print(b, end=' ') a, b = b, a+b fabs(1000) 方式二:列表 re ...

  5. 30.Nginx集群搭建笔记

    源码安装Nginx: tar -zxvf nginx-1.8.0.tar.gz -C /nginx/        #解压Nginx rpm -ivh keepalived-1.2.13-5.el6_ ...

  6. DSO的记录模式Record Mode字段测试

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  7. 欧拉回路(hdu3018)

    刚学图论不久,看着别人的博客慢慢学了一点基础的,感觉还是有点力不从心,感觉图论的题好多长得都很像,什么太监算法(Tarjan),Kosaraju,当然最基础的还是并查集...好了继续介绍这道题.... ...

  8. sql连表分页查询(存储过程)

    1.平时分页查询都比较多针对一个表的数据 而这个分页查询是针对连表查询的 ,这也是我网上改版别人的sql语句 先在数据库新建一个存储过程 拷贝以下代码 CREATE PROCEDURE [dbo].[ ...

  9. Codeforces Round #389 (Div. 2, Rated, Based on Technocup 2017 - Elimination Round 3) D. Santa Claus and a Palindrome STL

    D. Santa Claus and a Palindrome time limit per test 2 seconds memory limit per test 256 megabytes in ...

  10. iOS - Phone 电话

    1.调用电话 1.1 拨打系统电话 调用系统自带的打电话程序,要跳转到打电话程序,打完电话自动跳转回来. 在 iOS9.0 + 系统隐私控制里禁止查询设备中已安装的 App,所以在 iOS9.0 + ...