返回 登录
0

Java单例设计模式(Singleton)

面向对象的程序设计中,有时候,我们需要一些对象只有一个,比如线程池(threadpool),缓存(cache),对话框,等等。这些示例也只能有一个,如果有很多个,就会造成数据不统一,资源使用过量的结果。

在Java中,使用“单例设计模式”最简单的方法是:在 class 中实例化一个对象;将构造函数私有化(没有公开的构造函数,用户就不能再创建另一个对象了);提供通过 class 获得对象的方法(static)。

另外,如果这个对象比较耗资源,我们在程序中又不一定会用到它的话,那就太浪费了。所以一般我们会在程序用到单例的时候才第一次实例化这个对象。

public class Singleton {
 private static SingletonuniqueInstance;

 private Singleton(){}

 public static SingletongetInstance(){
 if(uniqueInstance == null){
 uniqueInstance = new Singleton();
 }
 return uniqueInstance;
 }
}

上面这种实现的方法可能存在的问题是:如果多线程访问,那么对象还没有被实例化之前,可能会有两个线程同时进入 getInstance() 方法,一下子出来两个对象。

只要对这个方法加上 synchronized 关键字,就可以轻易地解决这个问题了。 synchronized 关键字可以保证,每个线程在进入这个方法之前,都必须等其他进程退出。也就是说,不可能同时有两个线程进入这个方法。

public class Singleton {
 private static SingletonuniqueInstance;

 private Singleton(){}

 public static synchronized SingletongetInstance(){
 if(uniqueInstance == null){
 uniqueInstance = new Singleton();
 }
 return uniqueInstance;
 }
}

这样的确可以解决问题,但是又一个问题来了:性能降低。事实上,我们只需要第一次执行这个方法的时候,才需要同步。设置好单例对象,就不需要同步了,之后每次同步都是一种累赘。这个问题,有以下3个解决方法供参考:

1.如果 getInstance() 对程序的性能不是很重要,那么……就这样吧,这个方法简单又有效。

2.如果创建示例的代价不是很大,那么可以一开始就创建好示例。这样就可以解决多线程的问题(如此看来,并不是“用到时再创建”就一定好,也要看具体情况,实例化对象代价不大并且需要反复使用,那么就立即创建;如果对象消耗资源大且用到次数不多,再考虑惰性创建)。

public class Singleton {
 private static SingletonuniqueInstance = new Singleton();

 private Singleton(){}

 public static SingletongetInstance(){
 return uniqueInstance;
 }
}

3.用双重检查加锁,先检查实例是否创建,如果未创建,才进入同步代码块创建实例。这样的话,只需要进入同步一次。

public class Singleton {
 private volatile static SingletonuniqueInstance;

 private Singleton() {}

 public static SingletongetInstance() {
 if (uniqueInstance == null) {
 synchronized (Singleton.class) {
 if (uniqueInstance == null) {
 uniqueInstance = new Singleton();
 }
 }
 }
 return uniqueInstance;
 }
}

关于 volatile 关键字: volatile 修饰的变量,jvm虚拟机 保证从主内存加载到线程工作内存的值是最新的 。

在上面的代码中,用到单例的时候,多个线程可以同时检查是否存在对象,如果存在,以后都不用再进入同步代码了。只有第一次实例化的时候,才需要进入同步。即提高了性能,又节省了空间(惰性创建)。

学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:492139965 我们一起学Java!

评论