文章目錄
- 餓漢式
- 懶漢式
- 靜态内部類
- 容器管理
- Activity管理類
單例設計模式:
是一種最最常見的一種模式,保證整個程式中隻有一個執行個體,常用的場景一般都是一些特殊的類,比如:老闆,管理類等等(皮膚的管理,Activity的管理)。
套路:
- 構造函數私有,防止在外部 new 對象
- 内部必須提供一個靜态的方法,讓外部調用
餓漢式
/**
* 單例 - 餓漢式
*/
public class Singleton {
//随着類的加載就已經new了對象
private static Singleton mInstance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return mInstance;
}
}
懶漢式
/**
* 單例 - 懶漢式
*/
public class Singleton2 {
//隻有使用的時候才去new對象(第一次調用getInstance時初始化),可能更加高效
// private static Singleton2 mInstance = new Singleton2();
private static Singleton2 mInstance;
private Singleton2(){
}
public static Singleton2 getInstance(){
if(mInstance == null){
mInstance = new Singleton2();
}
return mInstance;
}
}
public class Singleton3 {
//但是 懶漢式單例模式 會有多線程并發問題,如果多線程調用還是會存在多個執行個體
private static Singleton3 mInstance;
private Singleton3() {
}
//雖說解決了線程安全的問題,但是又會出現效率的問題,
//每次都要經過同步鎖的判斷,這種模式一般不建議使用
public static synchronized Singleton3 getInstance() {
if (mInstance == null) {
mInstance = new Singleton3();
}
return mInstance;
}
}
/**
* Double Check Lock(DCL)
*
* DCL方式實作單例模式的優點是既能夠在需要時才初始化單例,又能夠保證線程安全,
* 且單例對象初始化後調用getInstance不進行同步鎖
*/
public class Singleton4 {
private static Singleton4 mInstance;
private Singleton4() {
}
public static Singleton4 getInstance() {
if (mInstance == null) {
synchronized (Singleton4.class) {
if (mInstance == null) {
mInstance = new Singleton4();
}
}
}
return mInstance;
}
}
問題:
- 開辟一塊記憶體空間
- 初始化對象
- 給變量指派,将mInstance對象指向配置設定的記憶體空間(此時mInstance就不是null了)
執行順序可能是1-2-3 ,也可能是1-3-2。 如果是後者,3執行完畢(mInstance不為null),2未執行(未初始化對象),此時切換到另一個線程B,因為mInstance不為null,線程B直接取走mInstance,再使用時就會出錯。
加上volatile 關鍵字
- 防止重排序
- 線程可見性 - 某一個線程改了公用對象(變量),短時間内另一個線程可能是不可見的,因為每一個線程都有自己的緩存區(線程工作區)
public class Singleton5 {
private static volatile Singleton5 mInstance;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (mInstance == null) {
synchronized (Singleton5.class) {
if (mInstance == null) {
mInstance = new Singleton5();
}
}
}
return mInstance;
}
}
volatile 小例子
public class VolatileTest {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while (true) {
if (td.isFlag()) {
System.out.println("------------------");
break;
}
}
// 執行結果? flag= true ------------------
}
}
class ThreadDemo implements Runnable {
private volatile boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
flag = true;
System.out.println("flag=" + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
如果不加volatile,隻會輸出 “flag=true”
靜态内部類
靜态内部類單例模式
/**
* 單例設計模式-靜态内部類
*
* 隻有在第一次調用getInstance方法才導緻mInstance被初始化,
* 是以第一次調用getInstance方法會導緻虛拟機加載SingletonHolder類,
* 這種方式不僅能夠確定線程安全,也能夠保證單例對象的唯一性,同時也延遲
* 了單例的執行個體化,這是推薦使用的單例模式實作方式
*/
public class Singleton5 {
private Singleton5() {
}
public static Singleton5 getInstance() {
return SingletonHolder.mInstance;
}
public static class SingletonHolder {
private static final Singleton5 mInstance = new Singleton5();
}
}
容器管理
/**
* 單例設計模式 - 容器管理
* 系統的服務就是使用這種
*/
public class Singleton6 {
private static Map<String, Object> map = new HashMap<>();
private Singleton6() {
}
public static void registerService(String key, Object instance) {
if (!map.containsKey(key)) {
map.put(key, instance);
}
}
public static Object getService(String key) {
return map.get(key);
}
}
Activity管理類
實作方式有如下:
1 EventBus
2 廣播
3 集合管理
4 SingleTask
單點登入,彈框需要上下文,統一寫到BaseActivity中?
import android.app.Activity;
import java.util.Stack;
public class ActivityManager {
private static volatile ActivityManager mInstance;
// 集合用誰 List LinkedList Stack ?? 删除和添加比較多
private Stack<Activity> mActivities;
private ActivityManager() {
mActivities = new Stack<>();
}
// 雖說解決了線程安全的問題,但是又會出現效率的問題,
// 即保證線程的安全同是效率也是比較高的
// 這種方式其實還是會有問題?
public static ActivityManager getInstance() {
if (mInstance == null) {
synchronized (ActivityManager.class) {
if (mInstance == null) {
mInstance = new ActivityManager();
}
}
}
return mInstance;
}
/**
* 添加統一管理
*/
public void attach(Activity activity) {
mActivities.add(activity);
}
/**
* 移除解綁 - 防止記憶體洩漏
*/
public void detach(Activity detachActivity) {
// for 去移除有沒有問題? 一邊循環一邊移除會出問題 ,
// 既然這個寫法有問題,自己又想不到什麼解決方法,參考一下别人怎麼寫的
/*for (Activity activity : mActivities) {
if(activity == detachActivity){
mActivities.remove(activity);
}
}*/
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
*/
public void finish(Activity finishActivity) {
// for 去移除有沒有問題?
/*for (Activity activity : mActivities) {
if(activity == finishActivity){
mActivities.remove(activity);
activity.finish();
}
}
*/
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) {
// for 去移除有沒有問題?
/*for (Activity activity : mActivities) {
if(activity.getClass().getCanonicalName().equals(activityClass.getCanonicalName())){
mActivities.remove(activity);
activity.finish();
}
}*/
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() {
}
/**
* 擷取目前的Activity(最前面)
*/
public Activity currentActivity() {
return mActivities.lastElement();
}
}