在java中存在很多通用的设计模式,今天我简单总结一下单例设计模式:
解决问题:解决一个类在内存中只存在一个对象的问题(比如说一个软件中的配置文件)
一、如何保证对象的唯一性:
1、为避免建立过多的该类对象,应首先禁止其他应用程序创建该类对象。
2、为让其他应用程序访问到该对象,在本类中自定义一个对象,为避免直接访问该对象,要对其进行私有化。
3、提供访问方式,便于其他程序对自定义对象的访问,提供的访问方法是公有的。
对象保证是惟一的了,那么该如何具体实现呢?
二、使对象唯一性的步骤:
1、将构造函数私有化
2、在类中创建一个本类对象,并设置为私有的
3、提供一个公有的方法获取该对象,便于使用
三、单例设计模式的具体表现形式
具体代码如下:
1、饿汉式:先初始化对象,类一进内存就加载
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">//饿汉式
- class Single
- {
- private Single(){}
- private static Single s = new Single();
- public static Single getSingle()
- {
- return s;
- }
- }
- class SingleText
- {
- public static void main(String [] args)
- {
- Single s1 = Single.getSingle();
- Single s2 = Single.getSingle();
- if (s1==s2)
- System.out.println(true);
- else
- System.out.println(false);
- }
- }</span>
2、懒汉式:类进内存,对象还没有存在,只有调用了getSingle方法时,才建立对象
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">//懒汉式
- class Single
- {
- private Single(){}
- private static Single s = null;
- public static Single getSingle()
- {
- if (s==null)
- s = new Single();
- return s;
- }
- }
- class SingleText
- {
- public static void main(String [] args)
- {
- Single s1 = Single.getSingle();
- Single s2 = Single.getSingle();
- if (s1==s2)
- System.out.println(true);
- else
- System.out.println(false);
- }
- }</span>
运行的结果是:true;这是因为s1和s2引用的是同一个对象,所以符合条件。
但是对于第二种懒汉式的单例设计模式,会出现一些小小的问题,当一个线程调用时,是没什么问题的,如果多个线程调用此种方式,那么就会出现问题。
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">
- class Single
- {
- private static Single s = null;
- private Single(){}
- public static Single getInstance()
- {
- if (s == null)
- {
- synchronized(Single.class)
- {
- if (s == null)
- s = new Single();
- }
- }
- return s;
- }
- }</span>
比如说,当A调用时,当读到if(s1==null) 时,可能就停在这了,然后cpu再调用B,B也读到if(s1==null)这停下了,cpu再切换到A,接着创建一个对象,A就执行完了;之后B也向下执行,又创建一个对象;此时,对象就不唯一了,就破坏了对象的唯一性的初衷。那么解决方案是这样的:
这利用了锁的机制。synchronized是java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。这涉及到了多线程的问题。在这个例子中,比如说,当A调用时,当读到第二个if(s1==null) 时,可能就停在这了,然后cpu再调用B,B读到第一个if(s1==null)这停下了,因为加上synchronized后,A进去就相当于将其他的调用锁在外面的语句上了,要先执行完A,那么A执行完后,就已经创建了一个对象;当B再读到第二个if(s1==null)的时候不符合就直接结束了。如果再有其他C或D等调用的时候,就直接不符合第一个(s1==null)的条件,所以直接返回s。在这里,我们再来看看关于懒汉式的多线程问题:
上面的懒汉式的写法,是效率比较高的,先看看下面一段代码,比较一下,就会清晰很多:
[java] view plaincopyprint?
- <span style="font-family:Arial;font-size:12px;">
- class Single
- {
- private static Single s = null;
- private Single(){}
- public static synchronized Single getInstance()
- {
- if (s == null)
- s = new Single();
- return s;
- }
- }</span>
在这两种方式中,含有双重判断(称为第一种,无双重判断的为第二种)的效率更高,为什呢?虽然第一种和第二种都要先判断一下,但是对于第一种,第一个线程执行完后,s不为null了,那么后面只需要判断s是否为null即可,而对于第二种,要先判断锁,锁里没有线程,再进入,然后再判断一下s是否为null,这样一来,就要判断两次,所以,效率会更低。所以,对于双重判断,是可以提高效率的。
问题是解决了,但是相比之下,还是第一种饿汉式的单例设计模式更好一些,是一种建议使用的方式。