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

    1.饿汉式

    package demo5;
    
    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton(){}
        public static Singleton newInstance(){
            return instance;
        }
    }

    提供了一个静态实例并返回给调用者

    饿汉模式是最简单的一种实现方式,饿汉模式在类加载的时候就对实例进行创建,实例在整个程序周期都存在。优点是只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。缺点是即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。

    2.懒汉式

    public class Singleton{
        private static Singleton instance = null;
        private Singleton(){}
        public static Singleton newInstance(){
            if(null == instance){
                instance = new Singleton();
            }
            return instance;
        }
    }

    懒汉模式是在需要的时候才会被创建,如果已经创建了,那再次调用的时候就不会创建新的对象,而是返回之前创建的对象。但是懒汉模式并没有考虑线程安全问题,在多个线程可能会并发调用它的getInstance()方法,导致创建多个实例,因此需要加锁解决线程同步问题。如下

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

     3.双重校验锁

    上面加锁的懒汉式存在着性能问题,如果多次调用newInstance(),累计下来的性能损耗就比较大。因此就有了如下的双重校验锁。

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

    再次调用getInstance()只需要直接返回单例对象。所以大部分情况下,调用getInstance()都不会执行到同步代码块。不过还需要考虑一种情况,假如两个线程,一个线程执行了if (instance == null)语句,就会认为单例对象没有创建,此时另一个线程也执行了同样的语句,认为单例对象没有创建,然后两个线程依次执行同步代码块,各自创建了一个单例对象。所以需要在同步代码块中增加if (instance == null)语句。由于JVM底层模型原因,偶尔会出问题,不过JDK1.5及之后版本增加了volatile关键字。volatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的。

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

    4.静态内部类

    public class Singleton{
        private static class SingletonHolder{
            public static Singleton INSTANCE = new Singleton();
        }
        private Singleton(){}
        public static Singleton newInstance(){
            return SingletonHolder.INSTANCE;
        }
    }

    外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化,这样就不会占用内存。只有当newInstance第一次被调用时,才会初始化INSTANCE,第一次调用会加载SingletonHolder,这样不仅是线程安全的,也能保证单例的唯一性并且延迟了单例的实例化。

    5.枚举类

    public enum Singleton{
        instance;
        public void whateverMethod(){}    
    }

    枚举与普通类一样,可以有字段也可以有方法,并且枚举的实例创建是线程安全的,并且在任何情况都是单例的,但不能延时加载,虽然优点很多,但在主流开发中却很少用到。

     懒汉式和饿汉式在使用过程中都有一定的缺陷,在使用过程中一般不建议使用,双重校验锁和静态内部类很好的规避了前面两个的缺点,也是推荐的两种使用方式。

  • 相关阅读:
    sprintf与snprintf
    风雨20年:我所积累的20条编程经验
    istream_iterator, ostream_iterator,copy以及文件序列化
    [转载]关于C++,我觉得好的设计法则
    如何高效地管理时间
    B站上适合程序员的学习资源【赶紧收藏!】
    Redis和Memcached的区别
    Swoole的多进程模块
    Mac OS 查看 ip 地址及 DHCP 各 addr 含义
    mac将phpstorm 从主屏移动到副显示器(解决)
  • 原文地址:https://www.cnblogs.com/xiayq/p/10508254.html
Copyright © 2011-2022 走看看