单例模式

关于单例模式,先来说一个大家都知道的例子,spring中的Dao层用的就是单例,顾名思义,就是整个程序运行过程中,就只有一个dao,dao是和数据库打交道的,实际运行过程中,不可避免的会有多个线程访问数据库,这个时候如果不加什么措施的话,那么程序肯定是线程不安全的,想想看,一个线程刚刚获得了数据库的连接,另一个线程随之立刻获得了数据库的连接,那么前一个线程的数据库连接以及之后的执行就会被覆盖取消。。。

如何保证每个线程对数据库的操作相互独立呢?java提供了一个threadlocal对象,我们可以用它来实现在单例模式下线程之间的独立运行。

不光如此,spring中的action,service层配置一般默认都是单例模式,有这么一句话,单例是线程不安全的,这取决于对象是否包含成员变量,如果包含,比如说上面的dao包含对数据库的连接,这个时候需要额外的处理才能保证线程安全,如果没有成员变量,那么自然是线程安全的,比如说action,service(只做一般的方法调用)。

下面是单例模式的基础实现。

1 懒汉式

 1 public class SingleDemo1 {
 2     private static SingleDemo1 singleDemo1;
 3     private SingleDemo1(){    }
 4     public static SingleDemo1 getBean() {
 5         if(singleDemo1 == null)
 6         {
 7             singleDemo1=new SingleDemo1();
 8         }
 9         return singleDemo1;
10     }
11 }
View Code

2 饿汉式

1 public class SingleDemo1 {
2     private static SingleDemo1 singleDemo1=new SingleDemo1();
3     private SingleDemo1(){    }
4     public static SingleDemo1 getBean() {
5         return singleDemo1;
6     }
7 }
View Code

 两种实现有什么区别呢?懒汉式的初始化是在真正需要获取单例时发生的,而饿汉式则是在类加载时就已经初始化,两者存在于内存中的时间长短不一,第一次获取单例的等待时间长短不一,视情况而定。

另外,懒汉式的实现是线程不安全的,我们可以用synchronized来实现同步,相比于修饰方法,修饰代码块显然效率更高,因为我们可以只修饰可能导致线程不安全的那一段代码,修改如下:

 1 public class SingleDemo1 {
 2     private static volatile SingleDemo1 singleDemo1;
 3     private SingleDemo1(){    }
 4     public static SingleDemo1 getBean() {
 5         if(singleDemo1 == null)
 6         {
 7             synchronized (singleDemo1) {
 8             if(singleDemo1 == null){
 9                 singleDemo1=new SingleDemo1();
10             }
11             }
12         }
13         return singleDemo1;
14     }
15 }
View Code

至于为什么要volatile,则是为了保证单例对象是最新的,不然会导致第二个if判断没有意义。

上面只是单例的基础实现,实际应用中不可能像例子中这么简单,或许还会有一些状态等,比如说有些应用中会有local_cache这个变量,用来记录代码中经常需要用到的一些数据,这些数据会在不同的层中被调用,这个时候还需要通过使用threadlocal来保证每个线程的cache相互独立。

另外,在某些情况下,单例会失效,比如说反射,序列化。

posted @ 2016-07-01 10:01  痴生  阅读(271)  评论(0编辑  收藏  举报