您的位置:首页 > 编程语言 > Java开发

浅谈java中单例的几种写法

2017-11-01 18:30 387 查看
开发中或多或少都会用到单例模式,写法有很多种,这里收集整理了下:

1、懒汉式:

1)把构造方法定义成private的,不让外面new的方式生成实例;

2)提供一个static方法供外部调用,在这个方法里面返回实例;

public class SingleInstance {

private static SingleInstance singleInstance;

private SingleInstance() {
}

public static SingleInstance getInstance(){
if (null == singleInstance){
singleInstance = new SingleInstance();
}
return singleInstance;
}
}


只有在需要的时候,调用getInstance()方法才创建实例;

2、饿汉式

第一次调用的时候先生成实例,在通过getInstances()方法返回,是一种线程安全的写法;

public class SingleInstance {

private static final SingleInstance INSTANCE = new SingleInstance();

private SingleInstance() {
}

public static SingleInstance getInstance(){
return INSTANCE;
}
}


3、线程安全(synchronized)

懒汉式有个缺陷,就是无法处理并发的情况,试想有这样一个场景:

A线程第一次调用getInstance()获取一个实例,singleInstance为空,创建一个实例;

B线程第一次调用getInstance(),如果此时实例还未创建,singleInstance为空,就会在创建一个实例;

这样就会生成两个实例,修改下懒汉式的写法:

public class SingleInstance {

private static  SingleInstance singleInstance;

private SingleInstance() {
}

public synchronized static SingleInstance getInstance(){
if (null == singleInstance){
singleInstance = new SingleInstance();
}
return singleInstance;
}
}


在方法面前加上synchronized 同步锁保证线程安全。

4、双重检验锁

上面的写法优化一下,可以改写成这样:

public class SingleInstance {

private static volatile SingleInstance singleInstance;

private SingleInstance() {
}

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


多了一个判断,是不是看起来比上面那种写法逼格更高了;好吧这里简单解释下:

当有多线程调用getInstance()的时候,先判断实例是否为空,如果不为空则直接返回,没后面什么事了;

如果为空则进入synchronized 语句块,下面过程就和上面写法一样了;

至于volatile关键字是让在编译的时候按顺序执行。

5、静态内部类的方式

上面的饿汉式也有个弊端,如果我需要在外部调用SingleInstance的一个全局变量,在类加载时,会先初始化生成实例,这好像并不是我们想要的,好吧,那就换一种写法:

public class SingleInstance {

private SingleInstance() {
}

private static class SingleHolder{
private static final SingleInstance INSTANCE = new SingleInstance();
}

public static SingleInstance getInstance(){

return SingleHolder.INSTANCE;
}
}


这样就只在调用getInstance()方法的时候才去实例化。

6、反序列化的方式

这种写法是避免别人通过反射的方式来获取实例:

public class SingleInstance implements Serializable{

private SingleInstance() {
}

private static class SingleHolder{
private static final SingleInstance INSTANCE = new SingleInstance();
}

public static SingleInstance getInstance(){

return SingleHolder.INSTANCE;
}

private Object readResolve(){
return SingleHolder.INSTANCE;
}
}


实现Serializable接口,增加readResolve()方法,这里复制一波解释吧:”反序列化机制:在反序列化的时候会判断如果实现了serializable 或者 externalizable接口的类中又包含readResolve()方法的话,会直接调用readResolve()方法来获取实例。”

7、枚举方式

通过反序列化的方式只是避免别人通过反射的方式获取实例,但并不能彻底阻止;

public enum SingleInstance{
INSTANCE;

//... ...
}


因为枚举的特点,只会有一个实例,同时保证了线程安全、反射安全和反序列化安全。

以上总结基本就这些了,其中3、4可以看做是懒汉式的衍生体吧,5可以看做是饿汉式的衍生体;具体使用哪个自己考量了吧,我个人用的多的还是4和5,如果获取实例的时候需要传值选用第四种。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 单例模式