简述
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
特点
- 构造函数私有,防止在外部 new 对象
- 内部必须提供一个静态的方法,用于提供这个类的实例让外部调用
写法
单例模式的写法很多,包括饿汉式,饿汉式等,因为很多文章都有介绍这里我就不多提及。这里就着重记录下比较常用的这种写法。
静态内部类
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE =new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,在类进行初始化时,别的线程是无法进入的,所以在这里,JVM帮助我们保证了线程的安全性。
优点:避免了线程不安全,延迟加载,效率高。
双重检查(DCL)
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
写到这里先说一下volatile,在这我推荐一篇文章 点这里
我简单讲一下文章内容,主要涉及并发编程的三大概念:原子性,可见性,有序性。
1.原子性
即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。
2.可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。对于可见性,Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
3.有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
在Java里面,可以通过volatile关键字来保证一定的“有序性”。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
项目实践
public class ActivityManager {
private static volatile ActivityManager mInstance;
private Stack mActivities;
private ActivityManager(){
mActivities = new Stack<>();
}
public static ActivityManager getInstance() {
if (mInstance == null) {
synchronized (ActivityManager.class) {
if (mInstance == null) {
mInstance = new ActivityManager();
}
}
}
return mInstance;
}
/**
* 添加统一管理
* @param activity
*/
public void attach(Activity activity){
mActivities.add(activity);
}
/**
* 移除解绑 - 防止内存泄漏
* @param detachActivity
*/
public void detach(Activity detachActivity){
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
if (activity == detachActivity) {
mActivities.remove(i);
i--;
size--;
}
}
}
/**
* 关闭当前的 Activity
* @param finishActivity
*/
public void finish(Activity finishActivity){
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
if (activity == finishActivity) {
mActivities.remove(i);
activity.finish();
i--;
size--;
}
}
}
/**
* 根据Activity的类名关闭 Activity
*/
public void finish(Class extends Activity> activityClass){
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
if (activity.getClass().getCanonicalName().equals(activityClass.getCanonicalName())) {
mActivities.remove(i);
activity.finish();
i--;
size--;
}
}
}
/**
* 退出整个应用
*/
public void exitApplication(){
int size = mActivities.size();
for (int i = 0; i < size; i++) {
Activity activity = mActivities.get(i);
mActivities.remove(i);
activity.finish();
i--;
}
}
/**
* 获取当前的Activity(最前面)
* @return
*/
public Activity currentActivity(){
return mActivities.lastElement();
}
}
简要说下删除为什么不要使用foreach,如下
for (Activity activity : mActivities) {
if(activity == finishActivity){
mActivities.remove(activity);
activity.finish();
}
}
foreach遍历集合,其实是走的Iterator,首先判断hasNext(),如果没有了则终止循环,否则next()获取元素时,next()时,都要check一下集合元素个数是否变化了,如果变化了,则抛出异常。