zoukankan      html  css  js  c++  java
  • Java单例类的简单实现

        对于java新手来说,单例类给我的印象挺深,之前一道web后台笔试题就是写单例类。*.*可惜当时不了解。

        在大部分时候,我们将类的构造器定义成public访问权限,允许任何类自由创建该类的对象。但在某些时候,允许其他类自由创建该类的对象没有任何意义,还可能造成系统性能下降(因为频繁地创建对象、回收对象带来的系统开销问题)。例如,系统可能只有一个窗口管理器、一个假脱机打印设备或一个数据库引擎访问点,此时如果在系统中为这些类创建多个对象就没有太大的实际意义。因此,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。

        Java单例类有以下特点:

      1、单例类始终只能创建一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。

        实现要求:

      1、类的构造器使用private修饰,隐藏该类的所有构造器;

      2、根据良好封装原则,提供public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰;

      3、类必须缓存已经创建的对象,故需要一个static修饰的成员变量。

        先看一个单例类的经典实现:

    class Singleton 
    {    
        private static Singleton instance;
        private Singleton() {}
        public static Singleton getInstance()
        {
            if(instance == null)
                instance = new Singleton();
            return instance;
        }
    }
    public class SingletonTest
    {
        public static void main(String[] args)
        {
            Singleton s1 = Singleton.getInstance();
            Singleton s2 = Singleton.getInstance();
            System.out.println(s1 == s2);
        }    
    }

       通过上面的getInstance()方法提供的自定义控制,保证Singleton类只能产生一个实例。(这也是封装的优势:不允许自由访问类的Field和实现细节,而是通过方法来提供访问接口)所以在SingletonTest类的main方法中,看到两次产生的Singleton对象实际上是同一个对象,程序输出“true”。

          关于单例类的三种实现方式:饿汉式单例类、懒汉式单例类、登记式单例类(暂时还不懂*.*)

      1.饿汉式单例类:立即创建实例

    class Singleton1
    {
        //已经自行实例化 
        private static final Singleton1 instance = new Singleton1();
    
        private Singleton1() {}
        public static Singleton1 getInstance()
        {
            return instance;
        }
    }

      2.懒汉式单例类:在第一次调用时实例化

    class Singleton2 
    {
        //注意,这里没有final    
        private static Singleton2 instance=null;
    
        private Singleton2() {} 
        public static Singleton2 getInstance() 
        {
             if (instance == null) 
                 instance = new Singleton2(); 
            return instance;
        }
    }

      但上面这段程序在多线程环境中是不能保证单个实例的。例如下表所示多线程可能情况:

    时间点 线程1 线程2 instance值
    1 进入getInstance()方法   null
    2   进入getInstance()方法 null
    3 执行if(uniqueInstance == null)判断   null
    4   执行if(uniqueInstance == null)判断 null
    5 执行instance = new Singleton()   Singleton1
    6   执行instance = new Singleton() Singleton2
    7 执行return instance;   Singleton1
    8   执行return instance; Singleton2

      因此,便有了多线程安全的懒汉式单例类实现:

    class Singleton2 
    {  
        private static Singleton2 instance=null;
    
        private Singleton2() {} 
        public static synchronized Singleton2 getInstance() 
        {
             if (instance == null) 
                 instance = new Singleton2(); 
             return instance;
        }
    }

       因为通过给getInstance()方法增加synchronized关键字,也就是给getInstance()方法线程加锁,迫使每次只能有一个线程进入这个方法。但加锁的同步方法可能造成程序执行效率大幅度下降,因为对 Singleton2类来说,只有在第一次执行getInstance()方法时,才真正的需要对方法进行加锁同步,因为一旦第一次设置好instance 变量后,就不再需要同步这个方法了。之后每次调用这个方法,同步反而成了一种累赘。

      3.用"双重检查加锁",在getInstance()方法中减少使用同步:

    public class Singleton 
    {
    // volatile关键字确保当instance变量被初始化成Singleton实例时,多个线程正确地处理instance变量 private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance()
    {
    if (instance == null) {// 检查实例,如是不存在就进行同步代码区 synchronized (Singleton.class) {// 对其进行加锁,防止两个线程同时进入同步代码区 if (instance == null) {// 双重检查,非常重要,如果两个同时访问的线程,当第一线程访问完同步代码区后,生成一个实例;当第二个已进入getInstance方法等待的线程进入同步代码区时,也会产生一个新的实例 instance = new Singleton(); } } } return instance; } }

      参考:

      ITeye.com,java单例模式(Singleton pattern) : http://www.iteye.com/topic/652617

         

  • 相关阅读:
    面试题:安全点与安全区域
    面试题:手写LRU
    面试题:Spring循环依赖问题
    面试题:Spring4和Spring5的AOP顺序
    面试题:假如Redis里面有1亿个 key,其中有10 w个 key是以某个固定的 已知的前缀开头的,如果将它们全部找出来?
    VisionPro 标定与创建坐标系(Calibration and Fixturing)(二)
    VisionPro 标定与创建坐标系(Calibration and Fixturing)(一)
    VisionPro棋盘格标定测量实例
    VisionPro工业视觉的标定方法
    VisionPro (对相机进行标定)
  • 原文地址:https://www.cnblogs.com/chenbjin/p/3619003.html
Copyright © 2011-2022 走看看