Java中单例模式是最简单的设计模式之一,分懒汉式和饿汉式
两者本质上的区别就是在什么时候实例化类的 单例对象。
饿汉式在类加载时就初始化,容易浪费内存,也没有达到lazy loading的效果
懒汉式只有在需要的才初始化,但是线程不安全,无法在多线程下使用
针对该面试题,我们先看懒汉式和饿汉式的设计代码
饿汉式
public class Factory {
/**
*是否 Lazy 初始化:否
*是否多线程安全:是
*实现难度:易
*描述:这种方式比较常用,但容易产生垃圾对象。
*优点:没有加锁,执行效率会提高。
*缺点:类加载时就初始化,浪费内存。
*它基于 classloder 机制避免了多线程的同步问题,
* 不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,
* 在单例模式中大多数都是调用 getInstance 方法,
* 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,
* 这时候初始化 instance 显然没有达到 lazy loading 的效果。
*/
private static Factory instance = new Factory();
private Factory() {
System.out.println("**********" + Thread.currentThread().getName() + "-初始化" + Factory.class.getSimpleName() + "***************");
}
public static Factory getInstance() {
// if(instance == null ){
// instance = new Factory();
// }
return instance;
}
public void print() {
System.out.println("这是工厂的功能方法");
}
}
/**
* @author admin
*/
public class SomeTest {
public static void main(String[] args) {
for (int i=0;i<3;i++){
new Thread(()->{
Factory factory=Factory.getInstance();
factory.print();
},"线程"+i).start();
}
}
}
一开始就初始化了instance实例,所以不存在线程安全问题
饿汉式
public class Factory {
/**
*是否 Lazy 初始化:是
*是否多线程安全:否
*实现难度:易
*描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
*这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
*/
private static volatile Factory instance = null ;
private Factory() {
System.out.println("**********" + Thread.currentThread().getName() + "-初始化" + Factory.class.getSimpleName() + "***************");
}
public static Factory getInstance() {
if(instance == null ){
synchronized (Factory.class){
if(instance ==null) {
instance = new Factory();
}
}
}
return instance;
}
public void print() {
System.out.println("这是工厂的功能方法");
}
}
这块就是实现线程安全的代码
为什么有两个判空呢,而不直接在方法上面加synchronized呢
原因:
如果直接对整个方法加synchronized 会导致每个线程都会进入这个同步的if判断,如果此时有很多线程,会导致程序效率大大降低。
通过synchronized (Factory.class)同步代码块,仅对判断为空时,下面的new Factory()做同步处理,这样可以避免上述问题,但是这时候如果不在里面再判一次空的话,第一次在创建时可能会有几个线程同时进入(没创建是instance为null),所以同步块内部还需要一次判空并且把instance变量设置为volatile .
在多线程环境下,volatile是对其修饰的变量直接进行内存操作,而不创建副本