java单例设计模式总结
来源:互联网 发布:java迭代器的作用 编辑:程序博客网 时间:2024/05/16 19:35
说到设计模式,比较常见的是单例设计模式!
单例模式的特点:每一次实例化的对象都是同一个;
单例模式有两种实现方式:1、饿汉式2、懒汉式
饿汉式,就是还没一开始使用便初始化好。
public class Singleton {private static final Singleton singleton = new Singleton();private Singleton(){}public static Singleton getInstance() {return singleton;}}优点:预加载,避免了线程安全问题。
缺点:还没使用便加载好了,占用内存。
如果想等到程序一加载后再初始化,可以使用懒汉式。
public class Singleton {private static Singleton singleton = null;private Singleton(){}public static Singleton getInstance() {if (singleton == null) {singleton = new Singleton();}return singleton;}}优点:懒加载,一开始不占用资源。
缺点:会有线程安全问题。
考虑到线程安全问题,可以使用synchronized关键字修饰。
public class Singleton {private static Singleton singleton = null;private Singleton(){}public synchronized static Singleton getInstance() {if (singleton == null) {singleton = new Singleton();}return singleton;}}这样就解决了线程安全问题,但是由于同步的原因,性能不高,可以做如下优化
public class Singleton {private static Singleton singleton = null;private Singleton(){}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}}这样从代码的从代码的角度上看就问题,但是从JVM上分析,创建一个对象需要申请分配一块内存初始化构造函数;将指针指向分配的内存。这两个操作的先后顺序,JVM并没有规定好,特别是java编译后会将字节码进行重排序。所以可能会出现这样的情况:线程A先进来创建对象,将指针指向分配的内存,但是未完成初始化完成,线程B进来的时候,由于指针有引用singleton!=null,会直接未完成初始化好的对象,则会出现异常。
为了解决如上问题,可以考虑使用一个临时变量引用,来确保先初始化好再将指针指向内存的顺序,代码如下
public class Singleton {private static Singleton singleton = null;private Singleton(){}public static Singleton getInstance() {if (singleton == null) {Singleton temp;synchronized (Singleton.class) {temp = singleton;if (temp == null) {synchronized(Singleton.class) {if (temp == null) {temp = new Singleton();}}singleton = temp;}}}return singleton;}}问题其实还没完成解决,因为JVM编译优化可能将改变代码的顺序,如singleto = temp;放到同步代码块里面,所以需要重新做处理。
针对以上问题,终极解决方案有两种:
1、使用volatile
在JDK1.5的时候,出现volatile关键字,在并发的时候,当线程A的数据发生变化时,会通知其他线程修改数据这样能有效的避免JVM编译导致的重排序问题,即使出现线程A还没初始化完对象,线程B就进来的情况,也会通过volatile让线程A初始化好后通知线程B变更数据,代码如下:
public class Singleton {private volatile static Singleton singleton = null;private Singleton(){}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}}
2、使用静态匿名内部类
public class Singleton {private Singleton(){}private static class SingletonInstance {private static final Singleton singleton = new Singleton();}public static Singleton getInstance() {return SingletonInstance.singleton;}}由于代码没有static的属性,所以当Singleton被加载的时,内部类不会初始化,只有当getInstance()被调用时才会加载SingletonInstance,由于使用类加载的形式,并用final修饰,初始化对象的时候具有原子性,所以不要考虑线程安全,所以不需要加锁。
备注:通过反射我们可以强制生成多个对象,这些极端现象我们不做考虑,除了这样的方式,我们还可以使用序列化的方式强制生成多个对象,代码如下:
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class Singleton implements Serializable {private static final Singleton singleton = new Singleton();private Singleton(){}public static Singleton getInstance() {return singleton;}private Object readResolve(){return singleton;}public static void main(String[] args) throws Exception {Singleton s1 = null;Singleton s2 = getInstance();FileOutputStream fos = new FileOutputStream("d:/aa.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(s2);oos.flush();oos.close();FileInputStream fis = new FileInputStream("d:/aa.txt");ObjectInputStream ois = new ObjectInputStream(fis);s1 = (Singleton) ois.readObject();System.out.println(s1.hashCode() == s2.hashCode());}}上面的代码页面打印的结果是true,但是注释掉readResolve函数的时候,打印的结果会是false。
大家看完还有什么不懂或是觉得代码哪里有问题,欢迎留言交流。
0 0
- java单例设计模式总结
- java单例设计模式实例总结
- Java 设计模式—单例设计模式总结
- Java中的设计模式学习总结(二)---单例模式
- 总结:java设计模式之----单例模式singeton
- java设计模式总结之单例模式
- java设计模式总结三:单例模式
- Java设计模式(1)之单例模式学习总结
- 单例设计模式总结
- 单例设计模式总结
- 单例设计模式总结
- 单例设计模式总结
- java基础的零散总结(2)--单例设计模式
- JAVA常用设计模式总结(单例,工厂,抽象工厂)
- java之单例设计模式,继承,final关键字总结
- 黑马程序员:Java基础总结----单例设计模式
- 黑马程序员 知识点总结-Java单例设计模式
- 对于Java单例设计模式的总结和分析
- Ubuntu 14.04 编译Oneplus One Bacon CM13.0
- dubbo容器-注册中心
- CMakeListx.txt 编辑语法学习
- poj2282 poj3286 数位递归
- Android开发基础 startActivityForResult()、onActivityResult()和setResult()方法之间的联系
- java单例设计模式总结
- JSP九大对象
- 金额转换
- Python:基本运算、基本函数(包括复数)、Math模块、NumPy模块
- Sinacloud Cron
- Dubbo容器--Provider
- 107.LeetCode Binary Tree Level Order Traversal II(easy)[二叉树层次遍历 广度搜索 队列]
- dubbo容器-客户端调用
- 自定义Android图片轮播控件