1.单例设计模式
单例设计模式就是应用程序中一个类只存在一个对象实例。常用的场景一般都是一些特殊的类,比如:用户管理类,工具类,Activity管理类,EventBus等等。
2.使用套路
1. 构造方法私有化,防止在外部 new 对象
2. 内部必须提供一个静态的方法,让其外部调用
3. 单例常用的几种方式
3.1 单例——饿汉式
饿汉式特点:它在我们类加载的时候就进行创建对象
/**
* Created by ych on 2019/7/20.
* Description:单例设计模式:只要保证应用程序中该类只存在一个实例
* 单例套路:1.构造方法私有化(防止外部new 对象)
* 2.提供一个静态方法,让其外部调用
* 饿汉式特点:类一加载就会创建对象
*/
public class SingletonHungry {
private static SingletonHungry mInstance = new SingletonHungry();
//1.构造函数私有化
private SingletonHungry(){
}
public static SingletonHungry getInstance(){
return mInstance;
}
}
3.2 单例——懒汉式
懒汉式特点:它和饿汉式不同,它是当我们需要的时候进行创建对象,更加高效
/**
* Created by ych on 2019/7/20.
* Description: 懒汉式特点:当我们需要使用的时候才进行创建,更加高效
*/
public class SingleLazy {
private static SingleLazy mInstance;
private SingleLazy(){
}
private static SingleLazy getInstance(){
if (mInstance == null){
mInstance = new SingleLazy();
}
return mInstance;
}
}
3.3 单例——懒汉式(DCL)(常用)
DCL:即双重检查加锁(Double Check Lock)作用主要就是解决多线程并发问题,避免对象的多次创建。
比如:多个线程都访问该对象,每个线程会抢占我们CPU时间片,当我们线程1获取到时间片,判断mInstance == null,此时线程2获取到CPU时间片,也进行判断mInstance == null,这样就会导致创建多个对象
/**
* Created by ych on 2019/7/20.
* Description: 懒汉式,当我们使用的时候才会进行创建对象,可能更加高效
* 但是存在一些问题,多线程并发问题,如果多线程调用还是会创建多个对象
* 比如:多个线程都访问该对象,每个线程会抢占我们CPU时间片,当我们线程1获取到时间片,判断mInstance == null,此时线程2获取到CPU时间片,也进行判断mInstance == null,这样就会导致创建多个对象。
* 解决:使用synchronized同步锁,但是注意需要双重验证。
*/
public class SingleLazySync {
private volatile static SingleLazySync mInstance;
private SingleLazySync(){
}
/**
* 既保证线程安全,效率还是比较高的
* 注意:一定要在synchronized同步锁中再次进行判断,否则多线程下还是会创建多个对象。
* @return
*/
private static SingleLazySync getInstance(){
if (mInstance == null){ // 线程1 线程2都在这里排队了,如果去掉synchronized中的if判断还是会创建多个对象
synchronized(SingleLazySync.class){
if (mInstance == null){
mInstance = new SingleLazySync();
}
}
}
return mInstance;
}
}
3.4 单例——静态内部类(常用)
/**
* Created by ych on 2019/7/20.
* Description: 单例:静态内部类
*/
public class SingleStaticInClass {
private volatile static SingleStaticInClass mInstance;
private SingleStaticInClass(){
}
private static SingleStaticInClass getInstance(){
return SingleStaticInClassHolder.mInstance;
}
static class SingleStaticInClassHolder{
private static SingleStaticInClass mInstance = new SingleStaticInClass();
}
}
3.5 单例——容器管理(系统服务使用的方式,等下看下源码)
通过静态代码块进行初始化,代码块也是随着类的加载首先执行做一些进行初始化操作,使用集合进行存储
/**
* Created by ych on 2019/7/20.
* Description: 单例 -----容器管理,系统服务使用就是这个
*/
public class SingleSystemService {
private static Map<String,Object> mSingleMap = new HashMap<>();
static {
mSingleMap.put("layout_inflater",new SingleSystemService());
}
private SingleSystemService(){
}
/**
* 获取对象
* @param systemName
* @return
*/
private static Object getSystemService(String systemName){
return mSingleMap.get(systemName);
}
}
3.6 单例——容器管理(系统中使用LayoutInflater源码)
通过源码可知,我们常用的LayoutInflate其实也是一个单例的
/**
* Obtains the LayoutInflater from the given context.
*/
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
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;
}
/**
* ContextImpl(ContextImpl是Context的实现类) 中的 getSystemService方法
*/
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
/**
*
* 4.根据key 获取对应的服务 name 就是我们刚开始传入的 Context.LAYOUT_INFLATER_SERVICE = "layout_inflater"
*/
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
/**
* 1.Service容器,是一个静态的Map集合,通过key Context.LAYOUT_INFLATER_SERVICE = "layout_inflater"获取我们的LayoutInflate对象
*/
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
/**
* 2. 注册服务器
*/
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
/**
*3. 静态代码块,第一次加载该类执行 (只执行一次,保证实例的唯一性)
*/
static {
//...省略系统其他部分
//我们的LayoutInflate
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
//常见的ActivityService
registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
new CachedServiceFetcher<ActivityManager>() {
@Override
public ActivityManager createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
...
}
4.volatile关键字好处
4.1 防止重排序
SingletonHungry mInstance = new SingletonHungry()
我们创建一个对象都做了什么东西?
1.开辟一块内存
2.初始化对象
3.给变量进行赋值(把这个变量指向内存地址)
注意:如果是多线程的情况下,2和3的执行顺序是不固定的
如果3先执行了,此时mInstance已经不为空了,我们对象还未初始化完成,我们使用该对象的时候可能会出现问题,所以我们可以使用volatile关键字
4.2 线程可见性
当某一个线程修改了公用对象(变量),短时间内另一个线程可能是不可见的,因为每一个线程都有自己的缓存区(线程工作区)
4.2.1简单的测试
public class VolatileTest {
public static void main(String[] args){
//创建一个线程
VolatileThread volatileThread = new VolatileThread();
Thread thread = new Thread(volatileThread);
thread.start();
while (true){
if (volatileThread.isFlag()){
System.out.println("---------------");
break;
}
}
}
private static class VolatileThread implements Runnable{
private volatile boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
flag = true;
System.out.println("flag = " + isFlag());
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
}
可能大家都会觉得会输出:
flag = true
哈哈哈哈哈哈
注意:1.当我不添加 volatile 关键时只会打印 flag = true
2.当我添加 volatile 关键时才会打印 flag = true 哈哈哈哈哈哈
4.2.2效果
在这里插入图片描述5.总结
5.1 优点
1.单例模式一个应用程序中一个类只有一个实例对象,减少我们内存的开销,特别是频繁创建和销毁时,此时就可以使用单例模式
2.单例模式可以避免对资源的多重占用,比如一个写文件操作,由于只有一个实例存在内存中,避免对同一资源文件的同时写操作
5.2 缺点
1.单例对象如果持有Context,那么很容易造成内存泄漏,所有我们传递给单例对象的Context最好是Application Context
网友评论