版权声明:转载请标明原作者及地址 https://blog.csdn.net/cauchy6317/article/details/83212288
什么是单例模式?单例模式的作用是什么?单例模式如何实现?
Java语言中的对象都是通过类产生,如果没有类,就没有类的具体对象。那么,有类以后,我们就可以通过类产生很多实例对象。但是,有时候我们只需要该类产生且只能产生唯一的一个对象。比如,我的系统时间对象。因为这些对象在现实的需求中是被要求唯一的,而且这些对象的创建很耗费内存。对于这些对象的实现模式,我们称为单例模式。(笔者个人理解)
饿汉式:
/**
* @author chengjun
* @date 2018/10/11 21:19
* 单例模式——饿汉式(饿汉式顾名思义,不管程序实际运行的过程中需不需要此单例,该单利都会被创建)
*/
public class SingletonHungryMode {
// 这个单例模式的实例只能被内部的静态方法调用,所以要用static和private 修饰
private static SingletonHungryMode HUNGRYMODE = new SingletonHungryMode();
// 将构造器私有化是为了让该单例模式在外部无法被new出来
private SingletonHungryMode(){};
// 提供一个公开的静态方法(类方法),让外部获取该单例模式的实例对象
public static SingletonHungryMode getHUNGRYMODE() {
return HUNGRYMODE;
}
}
class test{
public static void main(String[] args) {
SingletonHungryMode singletonHungryMode0 = SingletonHungryMode.getHUNGRYMODE();
SingletonHungryMode singletonHungryMode1 = SingletonHungryMode.getHUNGRYMODE();
System.out.println(singletonHungryMode0==singletonHungryMode1);//true
}
}
由于静态方法只能访问到静态变量,所以单例的实例对象要被static关键字修饰。
懒汉式:
/**
* @author chengjun
* @date 2018/10/20 12:26
* 单例模式——懒汉式(懒汉式顾名思义,在程序实际运行的过程中需要此单例,该单利才会被创建)
*/
public class SingletonLazyMode {
//这个单例模式的实例只能被内部的静态方法调用,所以要用static和private 修饰
private static SingletonLazyMode LAZYMODE;
//将构造器私有化是为了让该单例模式在外部无法被new出来
private SingletonLazyMode(){}
//提供一个公开的静态方法(类方法),让外部获取该单例模式的实例对象
public static SingletonLazyMode getInstance(){
//获取单例对象时,判断该单例的实例对象是否为空
if(LAZYMODE == null){
LAZYMODE = new SingletonLazyMode();
}
return LAZYMODE;
}
}
这样在多线程的情况下,又看可能产生多个单例实例对象。
多线程下的单例模式:
public class SingletonLazyMode {
//这个单例模式的实例只能被内部的静态方法调用,所以要用static和private 修饰
private static SingletonLazyMode LAZYMODE;
//将构造器私有化是为了让该单例模式在外部无法被new出来
private SingletonLazyMode(){}
//提供一个公开的静态方法(类方法),让外部获取该单例模式的实例对象
public static SingletonLazyMode getInstance(){
//当线程运行到这里,会把SingletonLazyMode.class 锁起来,但是,每次线程过来都会被锁起来,降低了性能
//就是当线程A,线程B都过来了,线程A获得锁以后,线程B需要等待(性能降低)
synchronized (SingletonLazyMode.class){
//获取单例对象时,判断该单例的实例对象是否为空
if(LAZYMODE == null){
LAZYMODE = new SingletonLazyMode();
}
}
return LAZYMODE;
}
}
这样可以避免多线程下创建多个单例的实例对象,但是,锁机制在避免多线程创建实例对象的同时,也在获取此单例对象的过程加上了锁。所以对多线程获取该实例对象时,性能降低了很多。那么,我们要用到接下来的双重校验机制。
多线程的双重校验模式:
public class SingletonLazyMode {
//这个单例模式的实例只能被内部的静态方法调用,所以要用static和private 修饰
private static SingletonLazyMode LAZYMODE;
//将构造器私有化是为了让该单例模式在外部无法被new出来
private SingletonLazyMode(){}
//提供一个公开的静态方法(类方法),让外部获取该单例模式的实例对象
public static SingletonLazyMode getInstance(){
//通过在这里判空,可以在除了第一次创建实例对象以后,多线程获取该实例对象时避免锁机制
if(LAZYMODE == null){
//当线程运行到这里,会把SingletonLazyMode.class 锁起来,但是,每次线程过来都会被锁起来,降低了性能
//就是当线程A,线程B都过来了,线程A获得锁以后,线程B需要等待(性能降低)
synchronized (SingletonLazyMode.class){
//获取单例对象时,判断该单例的实例对象是否为空
if(LAZYMODE == null){
LAZYMODE = new SingletonLazyMode();
}
}
}
return LAZYMODE;
}
}
在这里双重校验机制的好处在于,除了第一次创建该实例对象的时候,线程会加上锁,以后的获取过程避免了锁机制。
静态内部类模式:
扫描二维码关注公众号,回复:
3692688 查看本文章
Java虚拟机加载静态内部类的线程是安全的。但是,不能防止反序列化。
public class SingletonLazyMode {
//将构造器私有化是为了让该单例模式在外部无法被new出来
private SingletonLazyMode(){}
//jvm加载类的过程是线程安全的,避免了首次加载创建该实例对象的多线程情况。从而保证了只创建一个实例对象
private static class SingletonLazyModeHandler{
private static SingletonLazyMode INSTANCE = new SingletonLazyMode();
}
//提供一个公开的静态方法(类方法),让外部获取该单例模式的实例对象
public static SingletonLazyMode getInstance(){
return SingletonLazyModeHandler.INSTANCE;
}
}
枚举模式:
实现了静态内部类的优点,同时防止了反序列化。
/**
* 利用枚举的方式实现单例,Android不推荐
*/
public enum EnumMode {
INSTANCE;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
总结:一般简单实现使用饿汉式;延迟加载使用静态内部类模式;防止反序列化使用枚举模式;对单例模式的创建过程进行控制的使用双重校验模式。