• 单例模式


    单例模式

    使用场景:

    1.要求生产唯一序列号

    2.WEB中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来

    3.创建的一个对象需要消耗的资源过多,比如I/O与数据库的连接

    单例模式的应用场景 :

    网站的计数器、应用程序的日志应用、数据路连接池(这个池子)、项目读取配置文件的类、Appliction(应用程序)、windows的任务管理器、回收站
     

    单例模式的实现

    public class SingleObject {
    
        private static SingleObject instance = new SingleObject();
    
        private SingleObject() {
        }
    
        public static SingleObject getInstance() {
            return instance;
        }
    
        public void showMessage() {
            System.out.println("Hello World");
        }
    
    
    }
    
    public class SingletonPattenDemo {
        public static void main(String[] args) {
    
            SingleObject object = SingleObject.getInstance();
    
            object.showMessage();
        }
    
    }
    

    单例模式的懒汉式实现

    描述:线程不安全的,延迟对象的创建,不支持多线程,严格意义上并不是单例模式

    public class SingleObject {
    
        public static void main(String[] args) {
            Bank bank1=Bank.getInstance();
            Bank bank2=Bank.getInstance();
            //是同一个对象
            System.out.println(bank1 == bank2);
            }
        }
    
    class Bank{
        //1.私有化类的构造器
        private Bank(){
        }
    
        //2.声明当前类对象,没有初始化
        private static Bank instance=null;
    
        //3.声明public,静态的返回当前类的对象的方法
        public static Bank getInstance(){
            if(instance==null){
                instance=new Bank();
            }
            return instance;
        }
    }
    

    单例模式的懒汉式实现(线程安全)

    描述:加了synchronized锁,保证单例,可以在多线程中很好的工作,但是加锁会影响效率

    public class SingleObject {
    
        public static void main(String[] args) {
            Bank bank1 = Bank.getInstance();
            Bank bank2 = Bank.getInstance();
            //是同一个对象
            System.out.println(bank1 == bank2);
        }
    }
    
    class Bank {
        private static Bank instance;
    
        private Bank() {
        }
    
        public static synchronized Bank getInstance() {
            if (instance == null)
                instance = new Bank();
    
            return instance;
        }
    
    }
    

    单例模式的饿汉式实现

    描述:最常见的实现方法,线程安全的,但对象加载时间过长,容易产生垃圾对象

    public class SingleObject {   
    	public static void main(String[] args) {
    
            Bank bank1=Bank.getInstance();
            Bank bank2=Bank.getInstance();
            //是同一个对象
            System.out.println(bank1 == bank2);
            }
        }
    class Bank{
        //1.私有化类的构造器
        private Bank(){
        }
    
        //2.内部创建类的对象,要求此对象也是静态的
        private static Bank instance= new Bank();
    
        //3.提供公共的静态方法,返回类的对象
        public static Bank getInstance(){
            return instance;
        }
    }
    

    双重校验锁的单例模式(double-checked locking DCL)

    描述:双锁机制,是线程安全的,保证了高性能。

    volatile关键字:保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
     
    Instance 采⽤ volatile 关键字修饰也是很有必要的
    Instance = new Singleton();
    这段代码其实是分为三步执⾏:

    1.为 Instance 分配内存空间
    2.初始化 Instance
    3.将 Instance 指向分配的内存地址
     
    但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成132。指令重排在单线程环境下不会出 现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤ getInstance() 后发现 Instance 不为空,因此返回 Instance,但此时 Instance 还未被初始化。 使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。

    public class Singleton{
        private Singleton(){} 
        public volatile static Singleton instance; //volatile不能少
        
        public static Singleton getInstace(){
            if(instance == null){  		//single checked
                synchronized(Singleton.class){
                    if(instance == null){ 	//double checked
                        instance = new Singleton();
                    }
                }
                return instance;
            }
        }
    	
    }
    

    选择题

    【单选】以下哪段代码,调用 getHelper 的过程,不是线程安全的

    A.

    
    public class LazyInitDemo {
        
        private static Helper HELPER = null;
        
        public static synchronized Helper getHelper() {
            if (HELPER == null) {
                HELPER = new Helper();
            }
            return HELPER;
        }
    }
    

    B.

    
    public class LazyInitDemo {
        
        private Helper helper = null;
    
        public Helper getHelper() {
            if (helper == null) {
                synchronized (this) {
                    if (helper == null) {
                        helper = new Helper();
                    }
                }
            }
            return helper;
        }
    }
    

    C.

    
    public class LazyInitDemo {  
        
        private static class HelperHolder {  
            private static final Helper HELPER = new Helper();  
        }  
        
        public static final Helper getHelper() {  
            return HelperHolder.HELPER; 
        }  
    }
    

    D.

    
    public class PreInitDemo{
    
        private static final Helper HELPER = new Helper();
        
        public static Helper getHelper(){
            return HELPER;
        }
    }
    

    A,单例模式的懒汉式实现,并且是线程安全的
    B,没有volatile关键字,不是线程安全的
    C,静态内部类实现单例模式,是线程安全的
    (这里参考:https://blog.csdn.net/qq_35590091/article/details/107348114?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1.no_search_link)
    D,单例模式的饿汉式实现,是线程安全的

  • 相关阅读:
    无缝滚动轮播图
    angular 1.6指令与服务
    angular 1.6路由
    javascript 对象
    JavaScript的运算符
    初识JavaScript!
    git常用命令(二)文字版
    CSS水平垂直居中
    收藏的一些牛逼博客
    html5 学习汇总
  • 原文地址:https://www.cnblogs.com/alwayszzj/p/15655220.html
Copyright © 2020-2023  润新知