单例模式——应用最广的模式(二)
来源:互联网 发布:淘宝卖家订单打印 编辑:程序博客网 时间:2024/04/30 00:36
定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
核心原理:将构造函数私有化,并通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。
实现单例模式主要关键点:
(1)构造函数不对外开放,一般为private;
(2)通过一个静态方法或者枚举返回单例类对象;
(3)确保单例类的对象有且只有一个,尤其是在多线程的环境下(比较困难);
(4)确定单例类对象在反序列时不会重新构建对象。
饿汉模式和懒汉模式区别:建立单例对象的时间不同。
1、饿汉模式:
public class Singleton{ //声明静态对象时就已经初始化,不管是否用得上 private static Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return singleton; }}
饿汉模式:无法解决在多线程环境下,单例类的对象有且只有一个,即线程安全。
2、懒汉模式:(不建议用)
public class Singleton{ private static Singleton instance; private Singleton(){} //每次都同步 public static synchronized Singleton getInstance(){ if(instance == null){ //只有在使用时才会被实例化,在一定程度上节约了资源 instance = new Singleton(); } return instance; }}
懒汉模式
优点:单例只有在使用时才被实例化,在一定程度上节约了资源;
缺点:(1)第一次加载时需要及时进行实例化,反应稍慢;
(2)最大是问题是每次调用getInstance都进行同步,造成不必要的同步开销。所以不建议使用。
3、双重检查锁定 :Double Check Lock (DCL) (可满足大部分需求)
public class Singleton{ private static Singleton mInstance = null; private Singleton(){} public static Singleton getInstance(){ if(mInstance == null){ synchronized(Singleton.class){ if(mInstance == null){ mInstance = new Singleton(); } } } return mInstance; } }
优点:(1)需要时才初始化单例;(2)保证线程安全,且单例对象初始化后调用getInstance不进行同步锁。可满足大部分需求。
缺点:
(1)加载反应慢;
(2)由于Java内存模型的原因偶尔会失败。(DCL失效)
(3)为避免失效,在JDK1.5之后,添加了volatile(不稳定),保证每次都是从主内存中读取;
private volatile static Singleton mInstance = null;
会损失一部分性能。但是这种优化,在《Java并非编程实践》中被指是丑陋的,不赞成使用。
4、静态内部类单例模式 (推荐使用)
public class Singleton{ private Singleton(){} public static Singleton getInstance(){ return SingletonHolder.mInstance; } /** *静态内部类 */ private static class SingletonHolder{ private static final Singleton mInstance = new Singleton(); }}
当第一次加载Singleton类时并不会初始化mInstance,只有在第一次调用Singleton的getInstance方法时才会导致mInstance被初始化。
因此,第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。推荐使用此法。
5、枚举单例
public enum SingletonEnum{ INSTANCE; public void doSomething(){ //doSomething }}
优点:默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。
why这么说呢,以上几种单例模式实现中,在一个情况下会出现创建对象的情况,那就是反序列化。
通过序列化可以将一个单例的实例对象写到磁盘,然后在读回来,从而有效地获得一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。
反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的、被实例化的方法readResolve(),这个方法可以让开发人员控制对象的反序列化。例如,上述几个示例中如果要杜绝单例对象在被反序列化时重新生成对象,那么必须加入如下方法:
private Object readResolve() throws ObjectSreamException { return mInstance;}
也就是在readResolve()方法中将mInstance对象返回,而不是默认的重新生成一个新的对象。而对于枚举,并不存在这个问题,因为即使反序列化它也不会重新生成新的实例。
6、使用容器实现单例模式
public class SingletonManager{ private static Map<String, Object> objMap = new HashMap<String, Object>(); private SingletonManager(){} public static void registerService(String key, Object instance){ if( ! objMap.containsKey(key) ){ objMap.put(key, instance); } } public static Objcet getService(String key){ return objMap.get(key); }}
可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
7、Android源码中的单例模式
在Android系统中,我们经常会通过Context获取系统级别的服务,如WindowsManagerService、ActivityManagerService等,更常用的是一个LayoutInflater的类,这些服务会在合适的时候以单例的形式注册在系统中,在需要的时候通过Context的getSystemService(String name)获取。
通常使用LayoutInflater.from(Context)来获取LayoutInflater服务,下面看看LayoutInflater.from(Context)的实现:
/** * Obtains the LayoutInflater from the given context. */ public static LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }
可以看出from(Context)函数内部调用的是Context类的getSystemService(String key)方法,跟踪到Context类可以看到,该类是抽象类:
public abstract class Context { //代码省略}
那么Context对象的具体实现类是什么呢?其实在Application、Activity、Service中都会存在一个Context对象,即Context的总个数为Activity个数 + Service个数 + 1。下面我们就以Activity中的Context来分析。
我们知道,一个Activity的入口是ActivityThread的main函数,在main函数中创建一个新的ActivityThread对象,并且启动消息循环(UI线程),创建新的Activity、新的Context对象,然后将该Context对象传递给Activity。
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); AndroidKeyStoreProvider.install(); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>");//主线程消息循环 Looper.prepareMainLooper();//创建ActivityThread对象 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
在main方法中,我们创建了一个ActivityThread对象后,调用了其attach函数,并且参数为false(非系统应用)。
private void attach(boolean system) { sCurrentActivityThread = this; mSystemThread = system; if (!system) {//不是系统应用 ViewRootImpl.addFirstDrawHandler(new Runnable() { @Override public void run() { ensureJitEnabled(); } }); android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", UserHandle.myUserId()); RuntimeInit.setApplicationObject(mAppThread.asBinder()); final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread);//关联mAppThread } catch (RemoteException ex) { // Ignore } // Watch for getting close to heap limit. BinderInternal.addGcWatcher(new Runnable() { @Override public void run() { if (!mSomeActivitiesChanged) { return; } Runtime runtime = Runtime.getRuntime(); long dalvikMax = runtime.maxMemory(); long dalvikUsed = runtime.totalMemory() - runtime.freeMemory(); if (dalvikUsed > ((3*dalvikMax)/4)) { if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024) + " total=" + (runtime.totalMemory()/1024) + " used=" + (dalvikUsed/1024)); mSomeActivitiesChanged = false; try { mgr.releaseSomeActivities(mAppThread); } catch (RemoteException e) { } } } }); } else { // Don't set application object here -- if the system crashes, // we can't display an alert, we just want to die die die. android.ddm.DdmHandleAppName.setAppName("system_process", UserHandle.myUserId()); try { mInstrumentation = new Instrumentation(); ContextImpl context = ContextImpl.createAppContext( this, getSystemContext().mPackageInfo); mInitialApplication = context.mPackageInfo.makeApplication(true, null); mInitialApplication.onCreate(); } catch (Exception e) { throw new RuntimeException( "Unable to instantiate Application():" + e.toString(), e); } } // add dropbox logging to libcore DropBox.setReporter(new DropBoxReporter()); ViewRootImpl.addConfigCallback(new ComponentCallbacks2() { @Override public void onConfigurationChanged(Configuration newConfig) { synchronized (mResourcesManager) { // We need to apply this change to the resources // immediately, because upon returning the view // hierarchy will be informed about it. if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) { // This actually changed the resources! Tell // everyone about it. if (mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(newConfig)) { mPendingConfiguration = newConfig; sendMessage(H.CONFIGURATION_CHANGED, newConfig); } } } } @Override public void onLowMemory() { } @Override public void onTrimMemory(int level) { } }); }
在attach函数中,参数为false的情况下,会通过Binder机制与ActivityManagerService通信,并且最终调用handleLaunchActivity函数,下面看下该函数的实现:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { //代码省略 Activity a = performLaunchActivity(r, customIntent);//代码省略 }
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //省略代码 Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); //1、创建Activity activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { //创建Application对象 Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); if (activity != null) { //2、创建Context对象 Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config);//3、将appContext等对象attach到Activity中 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { activity.setTheme(theme); } activity.mCalled = false;//4、调用Activity的onCreate方法 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } //省略代码 } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; }
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { int displayId = Display.DEFAULT_DISPLAY; try { displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token); } catch (RemoteException e) { }//5、创建Context对象,可以看到实现类是ContextImpl ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, displayId, r.overrideConfig); appContext.setOuterContext(activity); Context baseContext = appContext; //省略代码 return baseContext; }
通过1~5处注释的代码分析,Context的实现类为ContextImpl。
class ContextImpl extends Context { //代码省略 //ServiceFetcher通过getService获取服务对象 static class ServiceFetcher { int mContextCacheIndex = -1; //获取系统服务 public Object getService(ContextImpl ctx) { ArrayList<Object> cache = ctx.mServiceCache; Object service; synchronized (cache) { if (cache.size() == 0) { for (int i =0; i <sNextPerContextServiceCacheIndex; i++) { cache.add(null); } } else { //从缓存中获取Service对象 service =cache.get(mContextCacheIndex); if (service != null) { return service; } } service = createService(ctx); cache.set(mContextCacheIndex,service); return service; } } /* * 子类覆写该方法用来创建服务对象 */ public Object createService(ContextImpl ctx) { throw new RuntimeException("Notimplemented"); } } //1、Service容器 private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<String,ServiceFetcher>(); 2 private static int sNextPerContextServiceCacheIndex = 0; //2、注册服务器 private static void registerService(String serviceName, ServiceFetcher fetcher) { if(!(fetcher instanceof StaticServiceFetcher)) { fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; } SYSTEM_SERVICE_MAP.put(serviceName, fetcher); } //3、静态语句块,第一次加载该类时执行(只执行一次,保证实例的唯一性) static { //代码省略 //注册 registerService(LAYOUT_INFLATER_SERVERCE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflate(ctx.getOuterContext()); }}); //代码省略 } //4、根据key获取对应的服务 @Override public Object getSystemService(String name) { ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); return fetcher == null ? null : fetcher.getService(this); } //代码省略}}
从ContextImpl类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种ServiceFetcher,其中就包含了LayoutInflater Service。将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需根据Key来获取到对应的ServiceFetcher,然后通过ServiceFetcher对象的getService函数来获取具体的服务对象。当第一次获取是,会调用ServiceFetcher的createService函数创建服务对象,然后将该对象缓存到一个列表中,下次在取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。这种模式就是上面提到的使用容器实现的单例模式。
1 0
- 单例模式——应用最广的模式(二)
- Android 设计模式の单例模式——应用最广的模式
- Android应用最广的模式-单例模式
- 应用最广的模式--单例模式
- 应用最广的模式-单例模式
- 设计模式之单例模式——应用最广泛的设计模式
- 应用最广的模式-单列模式(结合Android源码)
- 单例模式——最常用的设计模式
- Android中使用很广的模式-单例模式
- 设计模式——单例模式(最常用)
- 设计模式(二)——单例模式(Singleton)
- 设计模式——单例模式(二)
- 设计模式之——单例模式(二)
- 工厂方法模式--应用最广(创建型模式02)
- Java设计模式之二——四种不同的单例模式(Singleton)
- php 的单例模式(二)
- 单例模式的应用
- 单例模式的应用
- Setting property 'source' to 'org.eclipse.jst.jee.server:webProject' did not find a matching propert
- ec++ 的笔记(三) ------ 资源管理
- Filter高级开发(四)——缓存数据到内存
- Qt中 QString和double等类型转换
- 创建博客-REST的资源
- 单例模式——应用最广的模式(二)
- java学习连接
- 关于fragment点击能穿透问题
- 2017TenXun校招(9-11)
- 一些基础-内建函数、循环、字典、类
- Android常用工具类
- JavaScript学习笔记之事件对象
- 【jzoj4771】【爬山】【人工栈】【图论】【强连通分量】
- Session与Cookie的详解及如何实现Session共享