这一节我们来看看单例的其他创建方法。
上一节的懒汉式单例,用到了方法的同步,这在多线程并发场景下,无疑是性能非常差的,因为这相当于串行化了,所以我们可以做一些改进,来提升一下懒汉式单例的性能。
懒汉式单例–改进
package com.itzhimei.study.design.singleton;
/**
* www.itzhimei.com
* 懒汉式单例 改进
*/
public class Singleton3 {
private static Singleton3 singleton = null;
private Singleton3() {
}
public static Singleton3 getInstance() {
if(singleton == null) {
synchronized(Singleton3.class) {
if(singleton == null) {
singleton = new Singleton3();
}
}
}
return singleton;
}
}
package com.itzhimei.study.design.singleton;
/**
* www.itzhimei.com
*/
public class Client3 {
public static void main(String[] args) {
Singleton3 s1 = Singleton3.getInstance();
Singleton3 s2 = Singleton3.getInstance();
if(s1 == s2) {
System.out.println("是一个对象");
} else {
System.out.println("不是一个对象");
}
}
}
输出:
是一个对象
改进的点就是将同步方法,修改为了同步代码块:
if(singleton == null) {
synchronized(Singleton3.class) {
if(singleton == null) {
singleton = new Singleton3();
}
}
}
这样多线程访问,都不会阻塞在方法入口,而是都可以进入到方法,同时如果实例不为空,那么就直接返回实例了,并没有阻塞的发生,没有串行化;如果实例还没有被初始化,那么就会进入同步代码块,这是串行的,但是也只会在首次创建实例的时候发生同步,后续都不会产生同步,所以性能就提升了。
注意:有的人可能会说这种写法由问题,单例new出来还没来得及分配内存空间就被使用了,需要加volatile来修饰成员属性,其实,这是在低版本java中才会发生,新版本的已经修复这个问题了。(炫技的前提是要真的懂,要不就秀脱了,面试时就丢人了)。
静态内部类实现单例
package com.itzhimei.study.design.singleton;
/**
* www.itzhimei.com
* 静态内部类单例
*/
public class Singleton4InnerClass {
private static Singleton4InnerClass singleton = null;
private Singleton4InnerClass() {
}
private static class SInnerClass {
private static final Singleton4InnerClass sic = new Singleton4InnerClass();
}
public static Singleton4InnerClass getInstance() {
return SInnerClass.sic;
}
}
package com.itzhimei.study.design.singleton;
/**
* www.itzhimei.com
*/
public class Client4 {
public static void main(String[] args) {
Singleton4InnerClass s1 = Singleton4InnerClass.getInstance();
Singleton4InnerClass s2 = Singleton4InnerClass.getInstance();
if(s1 == s2) {
System.out.println("是一个对象");
} else {
System.out.println("不是一个对象");
}
}
}
输出结果:
是一个对象
枚举类实现单例
其实枚举类天生就被Java设计成了单例,所以使用枚举类作为单例也是一种便捷的方法。
package com.itzhimei.study.design.singleton;
/**
* www.itzhimei.com
* 枚举单例
*/
public enum Singleton5Enum {
instance
}
应用场景
单例一般多用来做资源加载,唯一id生成器。
在实际应用中,我们最熟悉的就是spring中,依赖注入,默认都是单例的。包括早期用到struct框架的controller也是单例的。