Java单例模式与多线程
2016-07-24 12:50
465 查看
该篇博客,将会通过单例模式和多线程技术相结合的方式来学习,在此期间,我们会遇到之前没有考虑过的情况,那么,废话不多说,先来看看,实现单例模式的两种方法
此时打印结果如下:
![](https://img-blog.csdn.net/20160724103931695)
此时程序运行结果:
![](https://img-blog.csdn.net/20160724104842583)
可以看到我们在此方法上加入了synchronized关键字得到相同实例对象,但是这种方法的运行效率非常低下,是同步运行的,下一个线程想要取得对象,就必须要等到上一个线程释放锁之后,才可以。
同步代码块来解决
同步代码块可以针对某些重要的代码进行单独同步,而其他代码不需要同步,这样可以大幅提升效率。
![](https://img-blog.csdn.net/20160724105937098)
![](https://img-blog.csdn.net/20160724111556288)
可以看到,这里我们写入和读取的是不同的对象。
这里,我们在単例类中加入下面代码:
此时效果如下:
饿汉模式
public class MyObjectHungery { private static MyObjectHungery sMyObject = new MyObjectHungery(); private MyObjectHungery() { } public static MyObjectHungery getMyObjectInstance() { return sMyObject; } }
懒汉模式
public class MyObjectLazy { private static MyObjectLazy sMyObject = null; private MyObjectLazy() { } public static MyObjectLazy getMyObjectInstance() { if (sMyObject == null) { sMyObject = new MyObjectLazy(); } return sMyObject; } }
懒汉和饿汉模式的缺点
上面虽然使用”立即加载”和”延迟加载”实现了单例设计模式,但是在多线程下,根本不能实现 保持単例的状态。看下面的代码:public class MyObjectLazy { private static MyObjectLazy sMyObject = null; private MyObjectLazy() { } public static MyObjectLazy getMyObjectInstance() { try { if (sMyObject == null) { // 懒汉模式,模拟创建对象时候的一些耗时操作 Thread.sleep(3000); sMyObject = new MyObjectLazy(); } } catch (InterruptedException e) { e.printStackTrace(); } return sMyObject; } private static class MyThread extends Thread{ @Override public void run() { super.run(); // 通过懒汉模式获得当前単例对象,并打印其hashCode System.out.println("current object is : "+MyObjectLazy.getMyObjectInstance().hashCode()); } } public static void main(String[] args) { // 模拟多线程环境下,通过打印当前对象的hashCode,判断懒汉模式存在的多线程问题 MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); } }
此时打印结果如下:
懒汉模式的解决方案
声明synchronized关键字public synchronized static MyObjectLazy getMyObjectInstance() { try { if (sMyObject == null) { // 懒汉模式,模拟创建对象时候的一些耗时操作 Thread.sleep(3000); sMyObject = new MyObjectLazy(); } } catch (InterruptedException e) { e.printStackTrace(); } return sMyObject; }
此时程序运行结果:
可以看到我们在此方法上加入了synchronized关键字得到相同实例对象,但是这种方法的运行效率非常低下,是同步运行的,下一个线程想要取得对象,就必须要等到上一个线程释放锁之后,才可以。
同步代码块来解决
同步代码块可以针对某些重要的代码进行单独同步,而其他代码不需要同步,这样可以大幅提升效率。
public static MyObjectLazy getMyObjectInstance() { try { if (sMyObject == null) { // 懒汉模式,模拟创建对象时候的一些耗时操作 Thread.sleep(3000); synchronized (MyObjectLazy.class) { if (sMyObject == null) { sMyObject = new MyObjectLazy(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return sMyObject; }
使用静态内部类实现单例模式
public class MyObjectStatic { private static class GetStaticObj { private static MyObjectStatic sMyObjectStatic = new MyObjectStatic(); } private MyObjectStatic() { } public static MyObjectStatic getMyObjectInstance() { return GetStaticObj.sMyObjectStatic; } }
序列化与反序列化的単例模式
上面的代码,使用静态内部类虽然可以达到线程安全,但是如果遇到序列化和反序列化对象时候,还是多例的。看下面代码:public class MyObjectStatic implements Serializable{ private static final long serialVersionUID = -4956049192964237265L; private static class GetStaticObj { private static MyObjectStatic sMyObjectStatic = new MyObjectStatic(); } private MyObjectStatic() { } public static MyObjectStatic getMyObjectInstance() { return GetStaticObj.sMyObjectStatic; } private static class MyThread extends Thread{ @Override public void run() { super.run(); // 通过懒汉模式获得当前単例对象,并打印其hashCode System.out.println("current object is : "+MyObjectStatic.getMyObjectInstance().hashCode()); } } public static void main(String[] args) { // 将获得的単例模式序列化对象写入到文件里,并打印其hashcode try { MyObjectStatic myObjectStatic = MyObjectStatic.getMyObjectInstance(); OutputStream os = new FileOutputStream(new File("D:/object.txt")); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(myObjectStatic); os.close(); oos.close(); System.out.println("myObjectStatic write is :"+myObjectStatic.hashCode()); } catch (Exception e) { e.printStackTrace(); } // 从文件里读取写入的学序列化単例对象,并打印其hashcode try { InputStream is = new FileInputStream(new File("D:/object.txt")); ObjectInputStream ois = new ObjectInputStream(is); MyObjectStatic myObjectStatic = (MyObjectStatic) ois.readObject(); is.close(); ois.close(); System.out.println("myObjectStatic read is :"+myObjectStatic.hashCode()); } catch (Exception e) { e.printStackTrace(); } } }
可以看到,这里我们写入和读取的是不同的对象。
这里,我们在単例类中加入下面代码:
// 在反序列化中使用readResolve方法 private Object readResolve() { System.out.println("readResolve method runs ......"); return GetStaticObj.sMyObjectStatic; }
此时效果如下:
相关文章推荐
- 深入理解Java Servlet与Web容器之间的关系
- java DES加密解密源码
- Java集合框架(下)
- JDK 的 配置
- eclipse下maven项目的创建以及问题的解决
- java提高篇之关键字final
- Java三大框架之struts的多文件上传
- Java集合框架(四)_day18
- 把String 或者 log 写在根目录下的 .txt 里
- IO流之编码
- javaWeb-过滤器filter
- Java开源框架整合 iBase4J
- JAVA 性能优化
- java设计模式_工厂模式在开发中的运用
- Java反射机制的学习
- LFU cache Java
- 在Myeclipse10.7中移除项目对Hibernate的支持
- Java集合框架(上)
- java中数组的相关知识小结(推荐)
- 21. Merge Two Sorted Lists(Sort)