Java学习日记——单例模式中懒汉式的线程不安全问题

单例模式是一个重要的设计模式,我们通过单例模式可以得到一个相同的实例化对象。单例模式分为懒汉式和饿汉是,饿汉式比较简单,上来先创建一个私有化的类对象,通过一个公共的方法获取这个对象。而懒汉式则不同,不会自动生成,而是在方法中做一个判断,如果此时的实例化对象为空再去生成。
但是需要注意的是,懒汉式最大的问题就在于他是线程不安全的,看一个例子。


public class SingleDemo {
     
	public static void main(String[] args) {
     
		for (int i = 0; i < 3; i++) {
     
			new Thread(() -> {
     
				Lazy instance = Lazy.getInstance();
				System.out.println(instance);
			}, "线程对象" + i).start();
		}
	}
}

class Lazy {
     
	private static Lazy instance = null;

	private Lazy() {
     
		System.out.println("【单例】" + Thread.currentThread().getName() + "******实例化单例模式******");
	}

	public static Lazy getInstance() {
     
		if (instance == null)
			instance = new Lazy();
		return instance;
	}

	public void print() {
     
		System.out.println("helloworld");
	}
}

【懒汉】线程对象1******实例化单例模式******
【懒汉】线程对象2******实例化单例模式******
dataStructure.Lazy@2fd3b3a7
【懒汉】线程对象0******实例化单例模式******
dataStructure.Lazy@4d18dfdc
dataStructure.Lazy@62b79014


【饿汉】线程对象0******实例化单例模式******
dataStructure.Hunger@62b79014
dataStructure.Hunger@62b79014
dataStructure.Hunger@62b79014

在上面的结果中,饿汉式成功只创建了一个实例化对象,而懒汉式则创建了3个,这是为什么呢?
因为在多线程的程序中会出现好多个线程同时启动的状态,此时三个线程同时执行getInstance()方法,都得出了instance==null的结论,也就都实例化了一个对象。我们看一个解决方法。

改善我们的代码

public static synchronized Lazy getInstance() {
     
		if (instance == null)
			instance = new Lazy();
		return instance;
	}


【懒汉】线程对象0******实例化单例模式******
dataStructure.Lazy@62b79014
dataStructure.Lazy@62b79014
dataStructure.Lazy@62b79014

在上面的代码中我们添加了sychronized关键字,每次执行判断的时候锁其余线程,保证了我们的成功创建,但是这样的问题在于,我们每次都进行这样的一个判断,会让多线程的性能大大下降。我们可以做一点改动。


class Lazy {
     
	private static Lazy instance;

	private Lazy() {
     
		System.out.println(Thread.currentThread().getName() + "****** 线程执行 ******");
	}

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

这就是最终的懒汉式啦,在这个代码中我们进行了双重判断,在第一波线程涌入的时候我们使用sychnorized关键字锁线程,但是从此之后,我们有了instance对象,再进行判断就可以在外围将线程拦截,提升性能。

你可能感兴趣的:(java,设计模式,多线程,编程语言)