大家好,我是野猪。
题目:设计一个类,我们只能生成该类的一个实例。
第一种:只适用于单线程环境
单利模式最简单的也是不会用的写法
特征:只适用于单线程环境
问题:当2个线程同时运行到instance·是否为null的if语句,并且instance的确没有创建的时候 那么2个线程都会创建一个实例。
java代码:
public class Singleton1 {
private Singleton1() {
}
private static Singleton1 singleton1 = null;
public static Singleton1 getInstance() {
if (singleton1 == null) {
singleton1 = new Singleton1();
}
return singleton1;
}
}
第二种:多线程环境中能工作但是效率不高
双重加锁 适合多线程但是因为加锁耗时导致效率很低
特征:加上了同步锁
问题:由于在一个时刻只有一个线程能拿到同步锁,当A线程拿到同步锁的时候,B线程只能等待
当A线程发现没有创建实例就会去创建实例,代码走完,然后A线程释放同步锁;然后B线程家加上同步锁,并运行接下来的代码;
现在存在一个问题就是A线程加上同步锁并创建完实例后B线程完全没有必要再加锁走判断有没有实例了。 ps:加锁是一个非常耗时的操作,在没有必要的时候应尽量避免。
java代码:
public class Singleton2 {
private Singleton2() {
}
private static Singleton2 singleton2 = null;
public static Singleton2 getInstance() {
//加上同步锁
synchronized (Singleton2.class) {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
}
return singleton2;
}
}
第三种:加同步锁前后两次判断实例是否已经存在
特征:为了避免A线程获取到同步锁后创建了实例,然后释放锁;B线程获取到同步锁,接着走判断的是否创建实例的情况;因为加锁耗时。
加锁之前判断一次有没有实例;
在首次没有创建实例的情况下,现在A线程获取同步锁创建实例,释放锁;B线程线先判断有无实例,有则不用运行接下来的代码了。
Java代码:
public class Singleton3 {
private Singleton3() {
}
private static Singleton3 singleton3 = null;
public static Singleton3 getInstance() {
if (singleton3 == null) {
//加上同步锁
synchronized (Singleton3.class) {
if (singleton3 == null) {
singleton3 = new Singleton3();
}
}
}
return singleton3;
}
}
双重加锁是比较常用的一种写法,确保了多线程环境下只创建一个实例,并且用两个if判断提高效率。
第四种:饿汉式单列
特征:在类初始化的时候 已经自行实例化;
问题:并不是当你需要才实例化 降低内存的使用率
ps:Java中的静态代码块是在虚拟机加载类的时候,就执行的,而且只执行一次。
非静态代码块是在类new一个实例的时候执行,而且是每次new对象实例都会执行。
java代码:
public class Singleton4 {
private Singleton4() {
}
private static Singleton4 singleton4 = new Singleton4();
public static Singleton4 getInstance() {
return singleton4;
}
}
第五种:饿汉式单列的变种 静态代码块
特征:跟第四种是一样的 都是在类初始化时即实例化instance
java代码:
public class Singleton5 {
private Singleton5() {
}
private static Singleton5 singleton5 = null;
static {
singleton5 = new Singleton5();
}
public static Singleton5 getInstance() {
return singleton5;
}
}
第六种:静态内部类
特征:静态内部类 改善了3和4的饿汉式 因为3、4 都是类加载的时候就实例化instance 降低了内存使用效率。只有在真正需要的时候才会创建实例,提高空间使用效率
因为内部类Nested{}没有被主动使用,只有调用getInstance()的时候才会去实例化instance。
这就避免了类加载的时候就实例化instance
举例:假设实例化instance很消耗资源,而类中有其他的方法我需要调用,如果类一加载就实例化的话,那太消耗资源了。所以做到真正需要的时候才去创建实例最好。
java代码:
public class Singleton6 {
private Singleton6() {
}
public static Singleton6 getInstance() {
return Nested.instance;
}
private static class Nested {
public static Singleton6 instance = new Singleton6();
}
}
以上是自己简单总结的常见的单例模式的写法,希望对大家有所帮助。在上一篇中简单的说了一下单例模式存在的因素和使用的地方,链接如下: