博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式——单例模式
阅读量:4149 次
发布时间:2019-05-25

本文共 5608 字,大约阅读时间需要 18 分钟。

关于单例模式,主要是为了一个类每次在使用它的实例时,都是同一个实例对象。

单例模式一

public class MySingleton {            private static MySingleton instance = new MySingleton();            private MySingleton(){}            public static MySingleton getInstance() {          return instance;      }        }

上述的单例模式代码一定是线程安全的,并且每次调用都会返回同一个对象,线程安全的原因是,静态变量是在类的初始化过程当中赋值,这个类在调用的时候,如果没有对类进行初始化,就会进行初始化。唯一的缺点就是,如果我们程序当中一直没有调用getInstance方法,但是我们对这个类进行了初始化,会占用一定的内存空间,因此这种实现方式显然不是最优的,

单例模式二

public class MySingleton{             private static MySingleton instance = null;             private MySingleton(){}        static{          instance = new MySingleton();      }            public static MySingleton getInstance() {           return instance;      }   }

这个方式的实现和上面第一个的方式实现方式基本上一样,都是属于在类初始化时候进行了对象的实例化,但是有可能初始化的时候,我们并没有使用getInstance方法,因此就造成了空间上的浪费。注:静态变量和静态代码块都会在类初始化的时候尽心加载。

单例三

由于上面两个方式都存在内存浪费的一种情况,那我们就设计一种,在使用到这个对象的时候,在进行实例化的方式

public class MySingleton {            private static MySingleton instance = null;            private MySingleton(){}            public static MySingleton getInstance() {          if(instance == null){//懒汉式              instance = new MySingleton();          }          return instance;      }  }

很明显这种方式最大的问题就是线程不安全,考虑到线程安全,就有了实例四的实现方式,直接在方法上加synchronious

单例四

public class MySingleton {            private static MySingleton instance = null;            private MySingleton(){}            public synchronized static MySingleton getInstance() {          try {               if(instance == null){//懒汉式                   instance = new MySingleton();              }          } catch (InterruptedException e) {               e.printStackTrace();          }          return instance;      }  }

这种方式呢,即实现了线程安全,又省了空间,但是呢,也有它的问题synchronous这个锁加的范围太大了,那么接下来就有了下面的,同步代码块,只对需要加锁的地方进行加锁,用不到的都不加锁。

单例五

public class MySingleton {            private static MySingleton instance = null;            private MySingleton(){}             public static MySingleton getInstance() {          try {                if(instance == null){//懒汉式                                     //创建实例之前可能会有一些准备性的耗时工作                   Thread.sleep(300);                  synchronized (MySingleton.class) {                      instance = new MySingleton();                  }              }           } catch (InterruptedException e) {               e.printStackTrace();          }          return instance;      }  }

这个看起来已经很完美了,写个代码测试下

class MyThread extends Thread{    @Override    public void run() {        System.out.println(Singleton.getInstance().hashCode());    }}--------------------------------------------- public static void main(String[] args) {        MyThread[] thread = new MyThread[10];        for(int i = 0; i < thread.length; i++ ){            thread[i] = new MyThread();        }        for(int i = 0; i < thread.length; i++ ){            thread[i].start();        }}

上述打印出来的hash值并不相同,原因在于虽然我们对创建部分进行了加锁,但是需要二次检查

单例六

public class MySingleton {            //使用volatile关键字保其可见性      volatile private static MySingleton instance = null;            private MySingleton(){}             public static MySingleton getInstance() {          try {                if(instance == null){//懒汉式                                    //创建实例之前可能会有一些准备性的耗时工作                   Thread.sleep(300);                  synchronized (MySingleton.class) {                      if(instance == null){//二次检查                          instance = new MySingleton();                      }                  }              }           } catch (InterruptedException e) {               e.printStackTrace();          }          return instance;      }  }

这种情况下才能保证每次打印出来的对象都是同一个对象。但是以上我们介绍的所有的实现方式,在序列化和反序列化的时候,是否还能保证对象相同,我们看下测试代码

public static void main(String[] args) {        Singleton singleton = Singleton.getInstance();        File file = new File("C:\\Users\\Administrator\\Desktop\\MySingleton.txt");        try {            FileOutputStream fos = new FileOutputStream(file);            ObjectOutputStream oos = new ObjectOutputStream(fos);            oos.writeObject(singleton);            fos.close();            oos.close();            System.out.println(singleton.hashCode());        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        try {            FileInputStream fis = new FileInputStream(file);            ObjectInputStream ois = new ObjectInputStream(fis);            Singleton rSingleton = (Singleton) ois.readObject();            fis.close();            ois.close();            System.out.println(rSingleton.hashCode());        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }

这时候打印出来结果hash值并不相同,所以之前的单例实现还需继续修改,在MySingleton方法当中实现readResolve方法即可

//该方法在反序列化时会被调用,该方法不是接口定义的方法,有点儿约定俗成的感觉      protected Object readResolve() throws ObjectStreamException {          System.out.println("调用了readResolve方法!");          return MySingletonHandler.instance;       }

单例七

使用静态内部类

public class MySingleton implements Serializable {             private static final long serialVersionUID = 1L;        //内部类      private static class MySingletonHandler{          private static MySingleton instance = new MySingleton();      }             private MySingleton(){}             public static MySingleton getInstance() {           return MySingletonHandler.instance;      }  }

静态内部类不需要加锁,同时也能保证线程安全而且也不会浪费内存空间,原因在于,外部类在初始化的时候,如果没有使用到内部类,是不会对内部类进行初始化,只有当我们显示调用静态内部的静态变量或者静态方法时,才会对静态内部类进行初始化。

这里没有给出枚举的实现方式。

转载地址:http://cmvti.baihongyu.com/

你可能感兴趣的文章
127个超级实用的JavaScript 代码片段,你千万要收藏好(下)
查看>>
【web素材】03-24款后台管理系统网站模板
查看>>
Flex 布局教程:语法篇
查看>>
年薪50万+的90后程序员都经历了什么?
查看>>
2019年哪些外快收入可达到2万以上?
查看>>
【JavaScript 教程】标准库—Date 对象
查看>>
前阿里手淘前端负责人@winter:前端人如何保持竞争力?
查看>>
【JavaScript 教程】面向对象编程——实例对象与 new 命令
查看>>
我在网易做了6年前端,想给求职者4条建议
查看>>
SQL1015N The database is in an inconsistent state. SQLSTATE=55025
查看>>
RQP-DEF-0177
查看>>
MySQL字段类型的选择与MySQL的查询效率
查看>>
Java的Properties配置文件用法【续】
查看>>
JAVA操作properties文件的代码实例
查看>>
IPS开发手记【一】
查看>>
Java通用字符处理类
查看>>
文件上传时生成“日期+随机数”式文件名前缀的Java代码
查看>>
Java代码检查工具Checkstyle常见输出结果
查看>>
北京十大情人分手圣地
查看>>
Android自动关机代码
查看>>