Jva单例模式的几种坑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiong9999/article/details/84937987

Java单例模式的几种坑

是什么

在一个JVM进程中,一个类对应的实例对象有且只有一个。

为什么

因为在一个程序中,有些业务逻辑和流程是重复的、通用的,没有必要在每次执行时再进行new相同对象的操作。

优点

只进行一次new操作,没有对象的频繁创建和回收,提高了JVM的运行响应速度。尤其是在高并发的情况下,对程序的运行有很大的提升。

1、在多线程的场景中,如果单例对象是通用类还好,如果单例对象中有锁的情况,是非常影响程序的运行速率;
2、单例对象在创建时,不能传递参数,否则就不算是单例对象了。

创建步骤

1、一个私有的、全局的、静态的单例对象引用;
2、一个私有构造函数;
3、一个获取该引用的方法。

分类

基于类对象的创建时间不同,分为饿汉模式和懒汉模式两种


饿汉模式
–不管用不用,先加载了再说。

class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstace() {
        return instance;
    }
}

优点
1、运行快:不管程序在以后会不会用到,在类加载的过程中,就把这个单例对象创建好,如果在后续的运行中有用到,调用getInstace()就直接获取到这个单例对象了;
2、线程安全

1、程序启动慢,因为这个对象在程序启动时就创建了;
2、程序占用内存较多,如果程序中有多个这种方式编写的不同单例对象,堆中会有这些单例对象,但这些单例对象,有可能是在程序运行到某个分支才用到。


懒汉模式 (不推荐下面这个写法)
–什么时候用,什么时候再加载,即延迟加载。

扫描二维码关注公众号,回复: 4449537 查看本文章
class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static Singleton getInstace() {
    	if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点
只有在第一次被用到的时候才创建。

在多线程情况下并发调用getInstace(),会出现创建了多个实例,不能保证这个对象是单
例。

改进1(不推荐下面这个写法)

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


1、在多线程场景下,虽然能保证对象的单例性,却降低了程序的运行响应速度,因为每个要获取这个单例对象的线程都需要进行的锁的等待和获取;
2、在执行instance = new Singleton();这句话时,CPU实际上执行的是多条指令,也就会出现指令重排的情况,这样有可能出现线程1只给instance引用赋了值,但值还没有完全创建好,此时,线程2来了判断引用不是null就以后获取到这个单例对象的情况。

改进2

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

这样修改后,用双重检查机制来保证在多线程的情况下,可以正常高效运行;
1、只有在instance为null时才获取锁,减小了多线程要获取锁的情况;
2、通过volatile关键字,来防止CPU进行指令重排,保证这个关键字对应的内容能顺序执行。

改进3 (推荐)

class Singleton {
    //private volatile static Singleton instance;
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
	private Singleton() {
    }
    public static synchronized Singleton getInstace() {
        return LazyHolder.INSTANCE;;
    }
}

通过内部类的方式,来实现多线程下的安全。


总结:
从速度和响应时间上看,饿汉模式比较好;
从程序内存的使用上看,懒汉模式比较好。

猜你喜欢

转载自blog.csdn.net/xiong9999/article/details/84937987