单例模式

单例模式

使用场景:

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,单例模式的饿汉式实现,是线程安全的

原文地址:https://www.cnblogs.com/alwayszzj/p/15655220.html