5种单例模式的介绍与比较
来源:互联网 发布:中国云计算方案大会 编辑:程序博客网 时间:2024/06/06 12:37
今天要介绍一下java(和安卓)的5中单例模式及相互的比较。
下面我会通过代码进行详细的介绍和比较,代码中会带有注释,如果觉得在这里看比较乱,可以直接复制到自己创建的.java文件中进行阅读(会有错误提示,如果强迫症,将部分相同的函数用”//”注释就好了),代码如下:
package com.tcl.adclient.utils.others;import java.io.ObjectStreamException;import android.content.Context;import android.media.MediaPlayer;public class MMediaPlayer { /** * 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。 * volatile很容易被误用,用来进行原子性操作。对于volatile修饰的变量, * jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的。 * 您只能在有限的一些情形下使用 volatile 变量替代锁。 * 要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件: * 1.对变量的写操作不依赖于当前值。 * 2.该变量没有包含在具有其他变量的不变式中。 * 只有在状态真正独立于程序内其他内容时才能使用 volatile —— * 这条规则能够避免这些模式扩展到不安全的用例。 * 使用 volatile 变量次要原因是其性能:某些情况下,volatile 变量同步机制的性能要优于锁 **/ private volatile static MediaPlayer mp; private static MediaPlayer mps = new MediaPlayer(); private static Object lock=new Object(); private MMediaPlayer() { } /** * 1.1懒汉,线程不安全 * @return */ public static MediaPlayer getInstance() { if (mp == null) { mp = new MediaPlayer(); } return mp;// } /** * // 1.2.懒汉,线程安全,这种写法是对getInstance()加锁,绝大情况下都不需要同步,效率低 * @param context * @return */ public static synchronized MediaPlayer getInstance() { if (mp == null) { mp = new MediaPlayer(); } return mp; } /** * 2.饿汉,这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, * 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。 * @return */ public static MediaPlayer getInstance() { return mps;// } /** * 3.1.自己根据对synchronized的理解写出来的,这里的context可以保证此对象在当前运行环境中的同步单例,在安卓中效率比1.2高。 * 但第一次执行时在当前环境(调用此函数的Activity环境)加锁,且多线程中不能保证单例!要保证单例可以借鉴3.2的双重锁校验。 * 适用于安卓在主线程中使用。。粒度过大,不建议! * @param context * @return */ public static MediaPlayer getInstance(Context context) { if (mp == null) { synchronized (context) { mp = new MediaPlayer(); } } return mp; } /** * 3.2.双重校验锁 单例模式 不建议使用,类似于3.3和3.1,静态方法中synchronized但多了在同步块内部判断,效率比3.1高,比3.3低, * 这个方法表面上看起来很完美,你只需要付出一次同步块的开销,但它依然有问题。除非你声明_instance变量时使用了volatile关键字。 * 没有volatile修饰符,可能出现Java中的另一个线程看到个初始化了一半的_instance的情况, * 但使用了volatile变量后,就能保证先行发生关系(happens-before relationship)。 * 对于volatile变量_instance,所有的写(write)都将先行发生于读(read), * 在Java 5之前不是这样,所以在这之前使用双重检查锁有问题。现在,有了先行发生的保障(happens-before guarantee),你可以安全地假设其会工作良好。 * 另外,这不是创建线程安全的单例模式的最好方法,你可以使用枚举实现单例模式,这种方法在实例创建时提供了内置的线程安全。另一种方法是使用静态持有者模式(static holder pattern)。 * * 要实现真正的断面,你必须同步一个全局对象或者对类进行同步 * @return */ public MediaPlayer getMyInstance() { if (mp == null) { synchronized (MMediaPlayer.this) {//如果在静态函数中此句会编译错误:Cannot use this in a static context, //要改为MMediaPlayer.class if (mp == null) { mp = new MediaPlayer(); } } } return mp; } /** * 3.3. 比3.2的做法要好一点,虽然都是双重校验锁。 * 3.2中的加锁是针对类定义的,一个类只能有一个类定义,而同步的一般原理是应该尽量减小同步的粒度以到达更好的性能。 * @return */ public MediaPlayer getMyInstance() { if (mp == null) { synchronized (lock) { if (mp == null) { mp = new MediaPlayer(); } } } return mp; } /** * 4.静态内部类。这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程, * 它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果), * 而这种方式是Singleton类被装载了,instance不一定被初始化。 * 因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类, * 从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载, * 另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载, * 那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。 * @author mylab */ @SuppressWarnings("unused") private static class SingletonHolder { private static final MediaPlayer INSTANCE = new MediaPlayer(); private SingletonHolder(){} public static final MediaPlayer getInstance() { return SingletonHolder.INSTANCE; } } /** * 5.枚举, 在安卓中不建议使用,占用资源最多,效率最低! * 在JAVA中,这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊 * 个人认为1.5中才加入enum特性,不直观,在实际的JAVA开发中也很少见到有人使用。 * public enum Singleton { * INSTANCE; * public void whateverMethod() { * ... * } * } */ /** * 将对象置为空,再此之前请确保该对象已停止播放并释放相关资源 */ public static void setNull() { if (mp != null) { mp = null; } } /** * 为了实现流转换复写Object函数,如果不涉及反序列化操作不用复写 * @return * @throws ObjectStreamException */ private Object readResolve() throws ObjectStreamException { return mp; }}
0 0
- 5种单例模式的介绍与比较
- Adapter模式与Facade模式的比较
- 多种JS引擎的介绍与比较
- OLAP与OLTP的介绍和比较
- Glide介绍以及与Picasso的比较
- Glide介绍以及与Picasso的比较
- 装饰者模式,代理模式与适配器模式的比较
- 装饰者模式,代理模式与适配器模式的比较
- 工厂模式与抽象工厂的比较
- 静态工厂与策略模式的比较
- 关于Java的singleton模式的介绍,比较有用
- 一篇比较好的介绍(两种线程模式)
- B/S与C/S的介绍与比较
- java 模式对话框与非模式对话框的比较
- C/S模式与B/S模式的比较分析
- C/S模式与B/S模式的比较分析
- CS模式与BS模式的比较分析
- Android中的MVP模式与MVC模式的比较分析
- LeetCode-11.Container With Most Water
- java环境变量设置
- 数学
- [androiod]_[Service基本使用]
- 琐事MARK
- 5种单例模式的介绍与比较
- Java注解(Annotation)介绍
- mysql5.5配置半同步复制
- Jquery弹出框效果 自己下载看看是否搭配风格
- leetcode_c++:Maximum Product Subarray(152)
- Java 具体优势
- 关于FragmentTransaction addToBackStack无效解决方法
- linux使用su切换用户提示 Authentication failure的解决方法
- HttpClient 4.3教程(转载)