zoukankan      html  css  js  c++  java
  • 设计模式之单例模式Singleton(三创建型)

     1.什么事单例模式?

      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例

      单例模式有以下特点:
        1、单例类只能有一个实例。
        2、单例类必须自己创建自己的唯一实例。
        3、单例类必须给所有其他对象提供这一实例。

      单例模式主要分为:饿汉模式,懒汉模式。

      饿汉式和懒汉式区别:

      从名字上来说,饿汉和懒汉,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

      单例模式着重涉及到了一个线程安全性的问题。

      什么是线程安全?

      如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

      或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

     2.单例模式应用场景

    1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 

    2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

    3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。

    4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

    5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

    6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。

    7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

    8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统

    3.单例应用举例。

    主要实现与解释都写在了代码之中。

    /**
    * @FileName Singleton.java
    * @Package com.ali.pattern.singleton
    * @Description 
    * <p>这里稍微对这里涉及到的几个关键字做一下介绍:
    * 一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,
    * 因此不能将它cache在线程memory中。volatile是变量修饰符,
    * 而synchronized则作用于一段代码或方法。
    * volatile只是在线程内存和“主”内存间同步某个变量的值,
    * 而synchronized通过锁定和解锁某个监视器同步所有变量的值。
    * 显然synchronized要比volatile消耗更多资源。
    * 根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。
            final类不能被继承,没有子类,final类中的方法默认是final的。
            final方法不能被子类的方法覆盖,但可以被继承。
            final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
            final不能用于修饰构造方法。
            注意:父类的private成员方法是不能被子类方法覆盖的,
            因此private类型的方法默认是final类型的。
       static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,
            也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
            被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,
            它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,
       Java虚拟机就能根据类名在运行时数据区的方法区内找到他们。
            因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
            用public修饰的static成员变量和成员方法本质是全局变量和全局方法,
            当声明它类的对象市,不生成static变量的副本,而是类的所有实例共享同一个static变量。 </p>
    * @Author ali blog:http://www.cnblogs.com/accipiter
    * @Date 2016年1月19日下午1:21:46
    * @Version V1.0.1
    */
    package com.ali.pattern.singleton;
    
    /**
     * @ClassName Singleton
     * <p>但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的</p>
     * @Description TODO
     * @Date 下午1:21:46
     */
    public class Singleton {
        //饿汉
        private static Singleton sigleton1 = new Singleton();
        /**
        * @Title getInstanceE
        * @Description 饿汉模式,本身就是线程安全的。
        * @return 
        * @Return Singleton
        * @Throws 
        * @user Administrator
        * @Date 2016年1月19日
         */
        public static Singleton getInstanceE(){
            return sigleton1;
        }
        //懒汉
        private static Singleton singleton=null;
        private Singleton()    {
        }
        /**
        * @Title getInstance
        * @Description 线程不安全
        * @return 
        * @Return Singleton
        * @Throws 
        * @user Administrator
        * @Date 2016年1月19日
         */
        public static Singleton getInstance(){
            if(null==singleton){
                singleton=new Singleton();
            }
            return singleton;
        }
        /**
        * @Title getInstanceSafe
        * @Description 线程安全,每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,
        * @return 
        * @Return Singleton
        * @Throws 
        * @user Administrator
        * @Date 2016年1月19日
         */
        public static synchronized Singleton getInstanceSafe(){
            if(null==singleton){
                singleton=new Singleton();
            }
            return singleton;
        }
        /**
        * @Title getInstanceSafeD
        * @Description 双重锁定
        * 确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,
        * 同时避免了每次都同步的性能损耗
        * 
        * @return 
        * @Return Singleton
        * @Throws 
        * @user Administrator
        * @Date 2016年1月19日
         */
        public static Singleton getInstanceSafeD(){
            if(null==singleton){
                synchronized (Singleton.class) {
                    if(null==singleton){
                        singleton=new Singleton();
                        }
                    }
                }
            return singleton;
        }
        /**
        * @ClassName LazyHolder
        * @Description 静态内部类,既实现了线程安全,又避免了同步带来的性能影响
        * @Date 下午1:44:53
         */
        private static class LazyHolder{
            private static final Singleton singleton=new Singleton();
        }
        public static final Singleton getInstanceClass(){
            return LazyHolder.singleton;
        }
        
        public void operate(int i){
            System.out.println(i+"、操作!");
        }
        /**
         * @Title main
         * @Description TODO
         * @param args 
         * @Return void
         * @Throws 
         * @user Administrator
         * @Date 2016年1月19日
         */
        public static void main(String[] args) {
            Singleton.getInstance().operate(1);
            Singleton.getInstanceSafe().operate(2);
            Singleton.getInstanceSafeD().operate(3);
            Singleton.getInstanceClass().operate(4);
            Singleton.getInstanceE().operate(5);
        }
    
    }
     
  • 相关阅读:
    Tomcat临时目录及java.io.tmpdir对应的目录
    第一篇随笔
    面试
    近期小结-082714
    从头开始构建web前端应用——字符炸弹小游戏(一)
    web版ppt制作插件impress.js源码注释翻译
    .net web service Application_BeginRequest,记录请求数据
    微信APP支付,阿里云服务器,统一下单请求超时
    android仿ios圆弧边框背景
    google map 地址编码及反向地址编码
  • 原文地址:https://www.cnblogs.com/accipiter/p/5142333.html
Copyright © 2011-2022 走看看