单例模式

单例模式,顾名思义,在程序运行时有且仅有一个实例存在。最常见的一个应用场景就是网站访问量的计数器,试想如果允许创建多个实例,那还怎么计数,这个时候就得创建有且仅有的一个实例了。如何防止程序创建多个实例呢?首先就是不能直接new。不能new那就是要将构造函数实例化,那怎么来创建实例呢?我们还是从代码着手。

 1 package day_5_singleton;
 2 
 3 /**
 4  * 单例
 5  * @author turbo
 6  *
 7  * 2016年9月8日
 8  */
 9 public class Singleton {
10     private static Singleton instance;
11     
12     private Singleton(){
13     }
14     
15     public static Singleton GetInstance(){
16         if (instance == null){
17             instance = new Singleton();
18         }
19         
20         return instance;
21     }
22 }
 1 package day_5_singleton;
 2 
 3 /**
 4  * @author turbo
 5  *
 6  * 2016年9月8日
 7  */
 8 public class Main {
 9 
10     /**
11      * @param args
12      */
13     public static void main(String[] args) {
14         Singleton singleton = Singleton.GetInstance();
15     }
16 
17 }

这就是单例模式的实现,看着好像挺简单的。它和传统的工具类有什么区别呢?一般的工具类也会将构造函数设为private,但是工具类不保存状态,仅提供一些静态方法或静态属性让你使用,而单例类是有状态的

上面是针对单线程,单线程不会出现多个实例的情况,但是在多线程里就有可能会出现创建多个实例了。

不信我们来看。

首先,我们用单线程的方式来创建两个实例,看他们实际上是不是一个实例。

Singleton singleton1 = Singleton.GetInstance();
Singleton singleton2 = Singleton.GetInstance();
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());

程序输出:

image.png

两个实例的hash值一样,说明他们是同一个实例。

我们再来看多线程的结果是怎样的,首先创建一个线程,在线程里实例化对象并输出hash值。

 1 package day_5_singleton;
 2 
 3 /**
 4  * 多线程实例化单例类
 5  * @author turbo
 6  *
 7  * 2016年9月8日
 8  */
 9 public class ThreadTest implements Runnable {
10 
11     /* (non-Javadoc)
12      * @see java.lang.Runnable#run()
13      */
14     @Override
15     public void run() {
16         Singleton singleton = Singleton.GetInstance();
17         System.out.println(singleton.hashCode());
18     }
19 
20 }

测试代码:

Thread singleton1 = new Thread(new ThreadTest());
Thread singleton2 = new Thread(new ThreadTest());
singleton1.start();
singleton2.start();

输出结果:

image.png

输出结果为两个实例的hash值确实不一样,说明确实创建了两个不同的实例。怎么办呢?加锁。对临界区共享资源的访问进行互斥访问,当一个线程进入临界区时,加锁,另一个线程进入临界区时则等待直到该对象被释放。

修改单例类。

 1 package day_5_singleton;
 2 
 3 /**
 4  * 单例
 5  * 
 6  * @author turbo
 7  *
 8  *         2016年9月8日
 9  */
10 public class Singleton {
11     private static Singleton instance;
12 
13     private Singleton() {
14     }
15 
16     public static synchronized Singleton GetInstance() {  //synchronized关键字
17 
18         if (instance == null) {
19             instance = new Singleton();
20         }
21         
22         return instance;
23     }
24 }

仅需对构造实例的方法添加synchronized关键字即可。

测试代码不变,看结果会发现两个线程创建的两个实例的hash值一样,说明它们是同一个实例。这样我们不管是在单线程还是多线程,所创建的这个单例类在程序里确实有且仅有一个。

我们其实可以继续引申synchronized关键字是什么,在方法上加和在给一个程序段加有什么区别?这会在以后开设一个多线程专栏来系统的介绍一下Java多线程。

《再说单例模式的线程安全问题》

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据之美

关于 MySQL UTF8 编码下生僻字符插入失败/假死问题的分析

1、问题:mysql 遇到某些中文插入异常 最近有同学反馈了这样一个问题: ? 上述语句在脚本中 load 入库的时候会 hang 住,web 前端、命令行操作...

53090
来自专栏古时的风筝

SharePoint—用REST方式访问列表

REST的定义与作用                                                                      ...

22550
来自专栏浪淘沙

java实现输出文件夹下某个格式的所有文件

15110
来自专栏维C果糖

史上最简单的 MySQL 教程(五)「SQL 基本操作 之 表操作」

根据操作对象的不同,咱们可以将 SQL 的基本操作分为三类,分别为:库操作、表(字段)操作和数据操作。

357130
来自专栏企鹅号快讯

MyBatis之Mapper XML 文件详解(一)

MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进...

24050
来自专栏编码前线

设计模式之单例模式

单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内...

13130
来自专栏技术之路

设计模式:单例模式

今天来简单来说一下单例模式:它要确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 比如我们登录一个系统的时候要记录一个员工的信息,在整个系统运行...

23780
来自专栏钟绍威的专栏

初学File类

对File类的基本方法的理解 今天刚开始学了File类 一开始看思想编程看得迷迷糊糊的,之后受不了了,直接去看API文档 归纳: File->jav...

205100
来自专栏hbbliyong

c++/c 获取cpp文件行号跟文件名

编译器内置宏: 先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。 ANSI C标...

30270
来自专栏java架构师

【SQL Server】系统学习之一:表表达式

本节讨论的相关内容包括:视图、派生表、CTE、内联表值函数 场景:如果要查询一组数据(例如聚合数据,也就是几个表聚合在一起的数据),这些数据并未在数据库中以表的...

33860

扫码关注云+社区

领取腾讯云代金券