zoukankan      html  css  js  c++  java
  • 01_单例模式

    【单例模式定义】

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

    【关键点】

    1.定义一个private访问权限的构造方法,避免被其它类new出一个对象。

    2.自己可以new出一个对象,其他类可以通过getInstance()方法获得同一个对象。

    【优点】

    1.由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。

    2.单例模式只生成一个实例,减少了系统的性能开销,当一个对象产生需要较多资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动的时候直接产生一个单例对象,然后永久驻留内存的方式来解决。

    3.单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个文件同时写操作。

    4.单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如一个单例类来负责所有的数据表的映射处理。

    【使用场景】

    (在一个系统中,要求类有且只有一个对象的场景)

    1.要求生成唯一序列号的环境。

    2.在整个项目中需要一个共享访问点或共享数据。

    3.创建一个对象需要消耗的资源资源过多,如要访问IO和数据库等资源。

    4.需要定义大量的静态常量和静态方法(常见于工具类)的环境。(当然也可以直接声明为static的方式)

    【饿汉式】

    package com.Higgin.Single;
    
    public class HungrySingle {
        //
        private static final HungrySingle hungrySingle=new HungrySingle();
        //限制产生多个对象
        private HungrySingle(){};
      //通过该方法获得实例对象
    public static HungrySingle getInstance(){ return hungrySingle; } //类中的其它方法,尽量使static public static void doSomething(){ } }

    【懒汉式:线程不安全】

    package com.Higgin.Single;
    /**
     * 线程不安全的 懒汉式 单例模式
     */
    public class LazySingle {
        private LazySingle(){};
        
        public static LazySingle lazySingle=null;
        
        public static LazySingle getInstance(){
            if(lazySingle==null){
                lazySingle=new LazySingle();
            }
            return lazySingle;
        }
    }

    [ 普通懒汉式:线程不安全的例子 ]

    class LazySingle {
        private LazySingle(){};
        
        public static LazySingle lazySingle=null;
        
        public static LazySingle getInstance() throws InterruptedException{
            if(lazySingle==null){
                Thread.sleep(1000);   //这里刻意延迟1秒,增加多个线程进入该判断的几率
                lazySingle=new LazySingle();
            }
            return lazySingle;
        }
    }
    class MyThread implements Runnable{
        @Override
        public void run() {
            try {
                for(int i=0;i<10;i++){
                    LazySingle  l = LazySingle.getInstance();//每个线程都去获取10次单例模式的实例
                    System.out.println(Thread.currentThread().getName()+"======"+l.hashCode());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }   
        }
    }
    
    public class Test{
        public static void main(String[] args) {
            MyThread t= new  MyThread();
            //开启四个线程
            new Thread(t).start();
            new Thread(t).start();
            new Thread(t).start();
            new Thread(t).start();
        }
    }

    [ 运行结果 ]

    其hashcode值有多处不同,说明发生了多线程的处理异常

    【懒汉式:双重加锁(线程安全)】

    /**
     * 懒汉式:双重加锁 
     */
    public class LazySingle {
        private LazySingle(){}
        
        private static LazySingle singleton=null;
        
        public static LazySingle getInstance(){
            if(singleton==null){                  //第一个null判断
                synchronized(LazySingle.class){   //同步代码块,确保这里只有一个进程进入代码块
                    if(singleton==null){          //第二个null判断
                        singleton=new LazySingle();
                    }
                }
            }
            return singleton;
        }
    }

    [ 为什么要加第一个null判断 ]

    加第二个null判断很容易理解,为什么要加第一个null判断呢?

    对于单例设计模式,本质上要求我们只能执行一次" singleton=new LazySingle(); ",

    如果没有第一个null判断,所有线程都会来抢占锁,抢占成功的正常执行,未成功的要进行等待,每次只能有一个线程执行。

    如果加上第一个null判断,只要执行了一次" singleton=new LazySingle(); ",后续进入该方法的线程,无需等待同步代码块的锁,只要在第一个null判断执行结束后就可以往下走了,极大节约了性能。

    [ 验证加第一个null判断的性能 ]

    /**
     * 懒汉式:双重加锁 
     */
    class LazySingle {
        private LazySingle(){}
        
        private static LazySingle singleton=null;
        
        public static  LazySingle  getInstance() throws InterruptedException{
            if(singleton==null){                 //第一个null判断
                synchronized(LazySingle.class){
                    Thread.sleep(1);  //延迟1ms使性能的效果更明显
                    System.out.println("进入同步...");
                    if(singleton==null){          //第二个null判断
                        Thread.sleep(1000);
                        singleton=new LazySingle();
                    }
                }
            }
            return singleton;
        }
    }
    class MyThread implements Runnable{
        @Override
        public void run() {
            try {
                for(int i=0;i<10;i++){
                    LazySingle    l = LazySingle.getInstance();//每个线程都去获取10次单例模式的实例
                    System.out.println(Thread.currentThread().getName()+"======"+l.hashCode());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }   
        }
    }
    public class Test {
        //线程 启动 并且 插队
        public static void startJoin(Thread t){
            try {
                t.start();  //启动线程
                t.join();   //线程插队
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
        public static void main(String[] args) throws InterruptedException {
            MyThread t= new  MyThread();
            
            long begin=System.currentTimeMillis();
            
            //创建多个插队的线程
            for(int j=0;j<100;j++){
                startJoin(new Thread(t));
            }
            
            LazySingle l=LazySingle.getInstance();   //获取单例模式的
            System.out.println("main方法的线程======"+l.hashCode());
            
            long end=System.currentTimeMillis();
            
            System.out.println("【耗时:"+(end-begin)+" 毫秒】");
        }
    }

    [ 运行结果 ]

    [ 注释了第一个null之后的运行结果(性能下降) ]

     

    【静态内部类方式:线程安全】

    /**
     * 懒汉式:静态内部类 线程安全
     */
    public class LazySingle {
        private LazySingle(){}
        
        private static class LazySingleBuilder{
            private static LazySingle singleton=new LazySingle();
        }
        
        public static LazySingle getInstance(){
            return LazySingleBuilder.singleton;
        }
    }

     [ 分析 ]

    当getInstance()方法第一次被调用的时候,第一次读取LazySingleBuilder.singleton,静态内部类LazySingleBuilder类得到初始化,在这个类装载并初始化的时候,会初始化它的静态域,从而创建LazySingle的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并且由虚拟机来保证它的线程安全性。

  • 相关阅读:
    openjudge-NOI 2.6-1996 登山
    openjudge-NOI 2.6-1944 吃糖果
    openjudge-NOI 2.6-1808 公共子序列
    openjudge-NOI 2.6-1775 采药
    openjudge-NOI 2.6-1768 最大子矩阵
    openjudge-NOI 2.6-1759 最长上升子序列
    tyvj P1050 最长公共子序列
    动态规划-最长公共子序列
    动态规划-最长上升子序列
    HDU 1811 Rank of Tetris
  • 原文地址:https://www.cnblogs.com/HigginCui/p/6196501.html
Copyright © 2011-2022 走看看