java 单例场景_深入java单例模式

单例是应用开发中一种设计模式,主要应用场景为:当且仅当系统中只能保留一个对象时使用。本文提出4中可以在生产环境中使用的单例设计模式。推荐使用enum的方式。

应用场景

例如一下应用场景[1]:

1、 Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗?

2、网站的浏览人数统计,一般也是采用单例模式实现,否则难以同步。

3、应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

//todo

在joshua block 的《effective java second edition》 一书中给出了三种单例设计模式

1、采用静态变量:

public class TaskManager {

public static final TaskManager INSTANCE = new TaskManager ();

private TaskManager (){}

//...

}

这种写法使用了私有的构造方法。来保证只能有一个实例,但是这种方法也有例外情况,因为,你可以通过反射来调用私有构造方法。这个时候你可以抛出异常。以下代码仅作为参考。

public class TaskManager {

public static final TaskManager INSTANCE = new TaskManager();

private TaskManager() {

if (INSTANCE != null) {

try {

throw new Exception("An object already exists");

} catch (Exception e) {

e.printStackTrace();

}

}

}

//...

}

2、采用静态方法

public class TaskManager {

private static final TaskManager INSTANCE = new TaskManager();

private TaskManager() {}

public static TaskManager getINSTANCE() {

return INSTANCE;

}

//...

}

3、采用enum的方式

这种模式是目前最佳的,因为:

1、JVM会保证enum不能被反射并且构造器方法只执行一次。

2、此方法无偿提供了序列化机制,绝对防止反序列化时多次实例化。

3、运行时(compile-time )创建对象(懒加载) // todo 关于cmpile-time和run-time有时间我单独写一篇文章。

enum是jdk5的特性,现在(2017)web应用普遍在jdk6、7、8,所以可以放心使用。

目前最佳的方式是使用接口的方式(解耦):

interface Resource {

Object doSomething();

}

public enum SomeThing implements Resource {

INSTANCE {

@Override

public Object doSomething() {

return "I am a Singleton nstance";

}

};

}

class Demo {

public static void main(String[] args) {

System.out.println(SomeThing.INSTANCE.doSomething());

}

}

或者不使用接口的形式

public enum SomeThing {

INSTANCE;

public void doSomething() {

System.out.println("INSTANCE = " + INSTANCE);

}

}

class Demo {

public static void main(String[] args) {

SomeThing.INSTANCE.doSomething();

}

}

也有人用其他的方式,我对这种方法持强烈反对,具体可以参考文献4,以下代码仅做参考

class Resource {

}

public enum SomeThing {

INSTANCE;

private Resource instance;

SomeThing() {

instance = new Resource();

}

public Resource getInstance() {

return instance;

}

}

class Demo {

public static void main(String[] args) {

System.out.println(SomeThing.INSTANCE.getInstance());

}

}

在其他文章中有提到“懒汉”、“恶汉”的名词,其实懒汉主要就是"懒"加载[注:指在使用时装载,不使用时不进行装载]

有人提出这种懒汉设计

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

很显然这种设计线程不安全,一般不会使用。

有人又提出了懒汉改进的方法,使其线程安全。

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,因为是重量级锁,效率很低。

于是有人提出了双重校验锁机制,这个用的也比较多。

下面代码就是用double checked locking 方法实现的单例,这里的getInstance()方法要检查两次,确保是否实例INSTANCE是否为null或者已经实例化了,这也是为什么叫double checked locking 模式。

/**

* Singleton pattern example with Double checked Locking

*/

public class DoubleCheckedLockingSingleton{

private volatile DoubleCheckedLockingSingleton INSTANCE;

private DoubleCheckedLockingSingleton(){}

public DoubleCheckedLockingSingleton getInstance(){

if(INSTANCE == null){

synchronized(DoubleCheckedLockingSingleton.class){

//double checking Singleton instance

if(INSTANCE == null){

INSTANCE = new DoubleCheckedLockingSingleton();

}

}

}

return INSTANCE;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值