设计模式之单例模式

  无论什么开发中,设计模式都起着关键的作用,其中比较常用的当属单例了,所谓单例,就是让一个类在项目中只存在一个对象,即使用到这个类的地方很多,也只存在一个对象。但是为什么要这样呢,为什么只创建一个对象呢,多个不也行吗?这个就要结合实际来说了,有些对象我们确实只需要一个,比如说线程池、缓存、硬件设备,如果多个实例就可能会导致冲突,出现运行结果不一致的现象。

一、单例的基本框架

  当然,单例有很多种实现形式,最基本的框架大致如下:

 1 /**
 2  * 普通单例:
 3  * 只使用在单一线程,多线程时可能会出现多个对象
 4  * @author 刘伟 2015/10/13
 5  */
 6 public class SimpleSingleTon {
 7     private static SimpleSingleTon instance = null;
 8 
 9     public static SimpleSingleTon getInstance() {
10         if (instance == null) {
11             instance = new SimpleSingleTon();
12         }
13         return instance;
14     }
15 
16     private SimpleSingleTon() {
17     }
18 }

  上面代码即为单例的一个基本的实现形式,很轻松就能看出在程序中首次使用这个类的代码通过getInstance()方法就能实例化这个类从而获得类的对象,但以后再调用getInstance()时就不会实例化,而是直接获得已经存在的对象。

二、单例模式的优化

  这段代码在单一线程中执行是没有问题的,但如果是在多线程中就可能会出现两个或多个对象,试想一下如果恰好有两个线程同时进入了getIntance()得if语句里面,这时候就会实例化两次SimpleSingleTon,因为首次执行getInstance()时instance是null,所以这种情况是可能发生的。那么怎么避免这种情况发生呢,可以使用以下几种方法:

  • 方案一:急切创建对象

  这种方法是直接在单例类里面吧静态变量直接实例化,这样无论是多线程还是单线程都能保证只有一个对象了,缺点就是会对内存造成一定的浪费。

 1 /**
 2  * 单例优化--急切创建对象
 3  * 
 4  * @author codingblock 2015/10/13
 5  */
 6 public class SingleTon {
 7     private static SingleTon instance = new SingleTon();
 8 
 9     public static SingleTon getInstance() {
10         System.out.println("instance:" + instance);
11         return instance;
12     }
13 
14     private SingleTon() {
15 
16     }
17 }
  • 方案二:添加同步锁

  为了在多线程中不让两个线程同时执行getInstance()方法,可以为此方法添加一同步锁,这样就能避免此情况发生了。代码如下:

 1 /**
 2  * 单例优化--添加同步锁
 3  * 可以在多线程中运行
 4  * @author 刘伟 2015/10/13
 5  */
 6 public class SimpleSyncSingleTon {
 7     
 8     private static SimpleSyncSingleTon instance = null;
 9     
10     public static synchronized  SimpleSyncSingleTon getInstance() {
11         if (instance == null) {
12             instance = new SimpleSyncSingleTon();
13         }
14         return instance;
15     }
16     
17     private SimpleSyncSingleTon() {
18     }
19 }

  这样就可以保证在多线程中也只会创建一个对象,但同步锁是比较耗费资源的,如果在程序中频繁地获取对象,这样的话效率就大大地降低了。所以说,在单例中添加同步锁的方法比较适用于对对象获取不是很频繁地情况。

  • 方案三:双重检查加锁法

  首先需要在对象变量前面添加一个volatile关键字,这个是为了通知编译器线程安全用的。然后再getInstance检查两次,具体代码如下:

 1 /**
 2  * 单例优化--双重检查加锁法 
 3  * 可以在多线程中运行
 4  * @author 刘伟 2015/10/13
 5  */
 6 public class CheckAgainSingleTon {
 7 
 8     private volatile static CheckAgainSingleTon instance = null;
 9 
10     public static synchronized CheckAgainSingleTon getInstance() {
11         if (instance == null) {
12             synchronized (CheckAgainSingleTon.class) {
13                 if (instance == null) {
14                     instance = new CheckAgainSingleTon();
15                 }
16             }
17         }
18         return instance;
19     }
20 
21     private CheckAgainSingleTon() {
22     }
23 }

  通过这个方法程序在获取对象时无论怎么样都只会进入加锁区一次,例如最开始两个线程在竞争时,其中一个线程进入了加锁后创建了对象,以后所有的进程在执行getInstance方法时直接判断instance非null,就能直接返回对象了,不需要再进入加锁区了。这样即使程序频繁地获取对象也不会再进入加锁区了,相对第二种方法就大大节省了资源。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java工会

java设计模式-单例模式

1636
来自专栏阿杜的世界

Spring中bean的scope

Spring容器中的bean具备不同的scope,最开始只有singleton和prototype,但是在2.0之后,又引入了三种类型:request、sess...

702
来自专栏与神兽党一起成长

使用commons-pool管理FTP连接

在封装一个FTP工具类文章,已经完成一版对FTP连接的管理,设计了模板方法,为工具类上传和下载文件方法的提供获取对象和释放对象支持。

122
来自专栏Java技术栈

90 % Java 程序员被误导的一个性能优化策略

我们经常看到一些 Java 性能优化的书或者理念,说不要在循环内定义变量,这样会占用过多的内存影响性能,而要在循环外面定义。接触 Java 这么久以来,相信很多...

922
来自专栏C语言及其他语言

C 语言中的指针和内存泄漏

原文出处: IBM developerworks 引言 对于任何使用 C 语言的人,如果问他们 C 语言的最大烦恼是什么,其中许多人可能会回答说是指针和内存泄...

2664
来自专栏前端架构

图说js中的this——深入理解javascript中this指针

没搞错吧!js写了那么多年,this还是会搞错!没搞错,javascript就是回搞错!

602
来自专栏黄Java的地盘

WebSocket系列之二进制数据设计与传输

通过前三篇博客,我们能够了解在通过WebSocket发送数据之前,我们需要传递的数据是如何变成ArrayBuffer二进制数据的;在我们收到二进制数据之后,我们...

721
来自专栏java架构学习交流

java web方面的面试问题,Spring MVC方面的面试问题,摘自java web轻量级开发面试教程

下面列出Spring Web方面的常见问题,除此之外,大家也可以自己不断收集,不断提升。 问题1,你们的项目是如何搭建Spring Web框架的,具体而言,如何...

1988
来自专栏十月梦想

node处理机制,并发性测试

下面一个案例看一下非阻塞I/O处理机制,更好了解到node机制!其实三个特点就是一个事情,擅长I/O,擅长任务调度!

633
来自专栏coder修行路

Python并发编程协程(Coroutine)之Gevent

Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corpo...

26110

扫码关注云+社区