设计模式之单例模式

来源:互联网 发布:客户关系 软件 编辑:程序博客网 时间:2024/05/16 10:24
单件模式
  1. package designpatter.singleton;
  2. public class Singleton {
  3. //利用一个静态变量来记录Sinleton类的唯一实例
  4. private static Singleton uniqueInstance;
  5. //这里吧构造器申明为私有的,只有自Singleton类内才可以调用构造器
  6. private Singleton(){}
  7. //用getInstance方法实例化对象,并返回这个实例
  8. public static Singleton getInstance(){
  9. if(uniqueInstance ==null){
  10. uniqueInstance=new Singleton();
  11. }
  12. return uniqueInstance;
  13. }
  14. }


单件:

独一无二,他是利用单件模式构造出来的,这个模式让让他在任何时刻都是只有一个对象,比如:如果注册表设置的对象,不想这样的对象有多个拷贝,就可以用单例模式,确保程序中使用的全局资源只有一份。

常常被用来管理共享资源,例如数据连接或者线程池

  1. package designpatter.singleton;
  2. /**
  3. * 巧克力工厂
  4. */
  5. public class ChoolateBoiler {
  6. private boolean empty;
  7. private boolean boiled;
  8. //代码开始的时候,锅炉是空的
  9. public ChoolateBoiler() {
  10. empty=true;
  11. boiled=false;
  12. }
  13. public void fill() {
  14. if(isEmpty()){
  15. empty=false;
  16. boiled=false;
  17. }
  18. }
  19. //锅炉排出时,必须是满的(不可以是空的)而且是煮过的,排出完毕后,把empty标志设为true;
  20. public void drain(){
  21. if(!isEmpty()&&isBoiled()){
  22. empty=true;
  23. }
  24. }
  25. public void boil(){
  26. if(!isEmpty()&&!isBoiled()){
  27. boiled=true;
  28. }
  29. }
  30. public boolean isBoiled() {
  31. // TODO Auto-generated method stub
  32. return boiled;
  33. }
  34. public boolean isEmpty() {
  35. // TODO Auto-generated method stub
  36. return empty;
  37. }
  38. }

这里如何同事存在两个巧克力锅炉实例,可能将会发生很糟糕的事情,

单例模式:确保一个类只有一个实例,并提供一个全局访问点
  1. package designpattern.signleton;
  2. /**
  3. *
  4. * @author taojian
  5. * @time 2017年3月5日下午6:31:48
  6. * @ClassName ChoolateBoiler.java
  7. * @description 现代化的巧克力工厂具备计算机控制的巧克力锅炉。锅炉要做的事情就是把巧克力和
  8. * 牛奶融在一起,进入到下一个阶段,将普通类改写成单例模式
  9. */
  10. public class ChoolateBoiler {
  11. private boolean empty;
  12. private boolean boiled;
  13. private static ChoolateBoiler uniqueInstance;
  14. private ChoolateBoiler() {
  15. empty=true;
  16. boiled=false;
  17. }
  18. /**
  19. //代码开始的时候,锅炉是空的
  20. public ChoolateBoiler() {
  21. empty=true;
  22. boiled=false;
  23. }*/
  24. public static ChoolateBoiler getInstance(){
  25. if(uniqueInstance==null){
  26. uniqueInstance=new ChoolateBoiler();
  27. }
  28. return uniqueInstance;
  29. }
  30. public void fill() {
  31. if(isEmpty()){
  32. empty=false;
  33. boiled=false;
  34. }
  35. }
  36. //锅炉排出时,必须是满的(不可以是空的)而且是煮过的,排出完毕后,把empty标志设为true;
  37. public void drain(){
  38. if(!isEmpty()&&isBoiled()){
  39. empty=true;
  40. }
  41. }
  42. public void boil(){
  43. if(!isEmpty()&&!isBoiled()){
  44. boiled=true;
  45. }
  46. }
  47. public boolean isBoiled() {
  48. // TODO Auto-generated method stub
  49. return boiled;
  50. }
  51. public boolean isEmpty() {
  52. // TODO Auto-generated method stub
  53. return empty;
  54. }
  55. }


深入了解:
1.我们正在把某个类设计成自己管理的一个单独实例,同时也避免其他类再自行产生实例。想要取得单件实例,通过单件类是唯一的途径。

2.我们也提供对这个实例的全局访问点:当你需要实例时,向类查询,它会返回单个实例。前面的例子利用延迟实例化的方式创建单件,这种做法对资源敏感的对象特别重要。

问题继续研究:虽然将普通的巧克力锅炉改写成了单例模式,但是会出现问题,当在fill()的方法中加入原料。如何处理多线程的问题?
  1. /**
  2. *
  3. * @author taojian
  4. * @time 2017年3月5日下午6:45:22
  5. * @methodname getInstance()
  6. * @descripe 处理多线程的问题,只要把getInstance()改成同步的方法,多线程灾难几乎就可以轻易解决
  7. * 通过synchronized关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法,也就是说不可以有两个
  8. * 线程可以同时进入这个方法。
  9. */
  10. public static synchronized ChoolateBoiler getInstance(){
  11. if(uniqueInstance==null){
  12. uniqueInstance=new ChoolateBoiler();
  13. }
  14. return uniqueInstance;
  15. }

但是这里又会存在一个问题,只有第一次执行此方法时候,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了,之后每次调用的时候,同步就是一种累赘。

为了符合大多数Java应用程序吗,很明显,我们需要确保单例模式能在多线程的状况下正常工作。但是似乎同步getIntance()的方法会影响性能。下面有几种选择:

1.如果getInstance()的性能对应用程序不是很关键,就什么都别做
2.使用急切创建实例,而不是用延迟实例化的方法。这种方式也叫做饿汉式的单例模式
  1. public class SingletonTest2 {
  2. //在静态初始化器中创建单例,这段代码保证了线程安全
  3. private static SingletonTest2 uniqueInstance=new SingletonTest2();
  4. //这里吧构造器申明为私有的,只有自Singleton类内才可以调用构造器
  5. private SingletonTest2(){}
  6. //用getInstance方法实例化对象,并返回这个实例
  7. public static SingletonTest2 getInstance(){
  8. //已经有了实例了,直接使用它。
  9. return uniqueInstance;
  10. }
  11. }
3.用"双重检查加锁",首先检查是否实例已经创建,如果尚未创建,才进行同步,这里一来,只有第一次同步,这正是我们想要的。

  1. public class SingletonTest3 {
  2. //在静态初始化器中创建单例,这段代码保证了线程安全
  3. private volatile static SingletonTest3 uniqueInstance;
  4. //这里吧构造器申明为私有的,只有自Singleton类内才可以调用构造器
  5. private SingletonTest3(){}
  6. //用getInstance方法实例化对象,并返回这个实例
  7. public static SingletonTest3 getInstance(){
  8. if(uniqueInstance==null){
  9. synchronized (SingletonTest3.class){
  10. if(uniqueInstance ==null){
  11. uniqueInstance=new SingletonTest3();
  12. }}}
  13. //已经有了实例了,直接使用它。
  14. return uniqueInstance;
  15. }
  16. }
4.类加载的方式:延迟加载,线程安全,同步情况下效率高,不要进行同步控制,实现简单,不能防止反序列化
  1. package designpattern.signleton;
  2. /**
  3. *
  4. * @author taojian
  5. * @time 2017年3月5日下午6:14:46
  6. * @ClassName SingletonTest.java
  7. * @description 单例模式,类加载方式
  8. */
  9. public class SingletonTest4 {
  10. //这里吧构造器申明为私有的,只有自Singleton类内才可以调用构造器
  11. private SingletonTest4(){}
  12. //静态内部类,用于持有唯一的SingletonClass的实例
  13. private static class OnlyInstance{
  14. static private SingletonTest4 ONLY=new SingletonTest4();
  15. }
  16. public static SingletonTest4 getInstance(){
  17. return OnlyInstance.ONLY;
  18. }
  19. }
5.枚举方式:立即加载,线程安全,实现简单,防止反序列化。
  1. public enum SingletonTest5 {
  2. INSRANCE;
  3. public void f(){
  4. }
  5. }









0 0
原创粉丝点击