单例模式

单例模式,顾名思义,在程序运行时有且仅有一个实例存在。最常见的一个应用场景就是网站访问量的计数器,试想如果允许创建多个实例,那还怎么计数,这个时候就得创建有且仅有的一个实例了。如何防止程序创建多个实例呢?首先就是不能直接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());

程序输出:

两个实例的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();

输出结果:

输出结果为两个实例的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多线程。

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

 

posted @ 2016-09-09 00:07 OKevin 阅读(...) 评论(...) 编辑 收藏