单例模式是设计模式的一种,在应用中也是比较常见的。单例模式本身是比较简单的,但是如果使用在多线程环境时,就会造成一些意想不到的情况。多线程的应用现在也很普及,因此有必要了解单例模式在多线程环境下使用时会遇到的问题,以及使用多线程技术如何解决这些问题。
当单例模式应用在多线程环境中,我们要考虑的是:如何使单例模式遇到多线程时是安全的,正确的。
下面介绍单例模式结合多线程技术在使用时的相关知识。
1.单例模式
单例模式,简单来讲就是一个类只能构建一个对象的设计模式。
那么如何实现单例模式?
首先,单例模式创建的是类。类的创建是根据构造方法创建的,因此如果单例类有构造方法的话需要将构造方法私有化(禁止其他程序创建类的对象)。
其次,需要在本类中自定义一个对象(这个对象就是单例对象,禁止其他程序创建类的对象就要自己创建一个,否则就无法创建对象了)。
最后,提供一个可访问类自定义对象的类成员方法(对外提供该对象的访问方式)。
基于上面的解释,我们就可以写出单例模式的简单代码实现:
单例模式第一版:
public class Singleton{
private Singleton(){} //私有构造函数
private static Singleton instance =null;//单例对象
//静态工厂方法
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
为什么这样写呢?我们来解释几个关键点:
<1>和上面介绍的一样,要想让一个类只构建一个对象,自然不能让它随便去做new,因此Singleton的构造方法是私有的。
<2>instance是Singleton类的静态成员,也就是我们的单例对象。它的初始值可以写成null,也可以写成new Singleton()。
<3>getInstance是获取单例对象的方法。
那么为什么单例模式中的单例对象和访问单例对象的方法要设置成静态的呢?
要想实现单例,我们不能用该类在其他地方创建对象,只能通过该类提供的方法访问类中的那个自定义对象。
那么关键来了,使用类中方法只有两种方式,①创建类的一个对象,用对象去调用方法;②使用类名直接调用类中方法。
显然第一种情况不能用,因为一个类只能创建一个对象,因此只能使用第二种方法。而想要使用类名直接调用类中方法,类中方法必须是静态的,因此访问单例对象的方法是静态的。而静态方法不能访问非静态态成员变量,因此类自定义的实例变量也必须是静态的,所以从语法上考虑是合适的。另外,类的静态成员变量就是指的类共享的对象,而单例模式的对象设成静态就是为了让该类所有成员共享同一个对象,所以把类的成员变量设置成静态的从语义上讲也是合适的。
在第<2>点中,如果单例对象的初始值是null,还未构建,则构建单例模式并返回。这个写法属于单例模式中的懒汉模式。
如果单例对象一开始就被new Singleton()主动构建,则不再需要判空操作,这种写法属于饿汉模式。
//饿汉模式
public class Singleton{
private Singleton(){} //私有构造函数
private static Singleton instance = new Singleton();;//单例对象
//静态工厂方法
public static Singleton getInstance(){
return ins