• 单例模式


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

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

  • 相关阅读:
    info命令简介
    scheme代码返回3个数中较大2个数之和
    mitscheme的标准库函数
    如何理解认知
    mitscheme编辑和装入程序文件
    scheme格式化输出字符串
    SQLServer中批量导出所有作业或链接脚本
    DAMA学习笔记(1)
    SQLServer AlwaysON修改可用性组的监听端口
    SQLServer将数据库置为只读
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/5854996.html
Copyright © 2020-2023  润新知