定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
核心原理:将构造函数私有化,并通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。
实现单例模式主要关键点:
(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函数创建服务对象,然后将该对象缓存到一个列表中,下次在取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。这种模式就是上面提到的使用容器实现的单例模式。