單例模式
簡(jiǎn)介
一個(gè)類(lèi)有且僅有一個(gè)實(shí)例,并且自行實(shí)例化向整個(gè)系統(tǒng)提供
基本用法
Kotlin 兩種:不帶參、帶參幅虑。
Java 六種:懶漢劳澄、餓漢童本、雙重校驗(yàn)鎖摄职、靜態(tài)內(nèi)部類(lèi)舔涎、枚舉和集合管理镜会。
Kotlin 不帶參
Kotlin 中使用 object
來(lái)創(chuàng)建單例,不允許有任何構(gòu)造函數(shù)终抽,可在 init
代碼塊中初始化
fun main() {
println(SingletonNormal == SingletonNormal) // init normal -> true
SingletonNormal.action() // action
}
object SingletonNormal {
init {
println("init normal")
}
}
Kotlin 帶參
fun main() {
println(Manager.getInstance("hello") == Manager.getInstance("singleton")) // init hello -> true
Manager.getInstance("singleton").action() // action
}
class Manager private constructor(name: String) {
init {
println("init $name")
}
companion object : SingletonHolder<Manager, String>(::Manager)
fun action() {
println("action")
}
}
open class SingletonHolder<out T, in A>(creator: (A) -> T) {
private var creator: ((A) -> T)? = creator
@Volatile
private var instance: T? = null
fun getInstance(arg: A): T {
val i = instance
if (i != null) {
return i
}
return synchronized(this) {
val i2 = instance
if (i2 != null) {
i2
} else {
val created = creator!!(arg)
instance = created
creator = null
created
}
}
}
}
餓漢式
加載時(shí)實(shí)例化
public class SingletonHungry {
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry() {
}
public static SingletonHungry getInstance() {
return instance;
}
}
懶漢式
使用時(shí)實(shí)例化
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {
}
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
雙重校驗(yàn)鎖
被volatile
修飾的變量的值戳表,將不會(huì)被本地線程緩存,所有對(duì)該變量的讀寫(xiě)都是直接操作共享內(nèi)存昼伴,從而確保多個(gè)線程能正確的處理該變量匾旭。
由于volatile關(guān)鍵字可能會(huì)屏蔽掉虛擬機(jī)中的一些必要的代碼優(yōu)化,所以運(yùn)行效率并不是很高圃郊。
public class SingletonLock {
private volatile static SingletonLock instance; // 關(guān)鍵字volatile
private SingletonLock() {
}
public static SingletonLock getInstance() {
if (instance == null) { // 先檢查實(shí)例是否存在
synchronized (SingletonLock.class) { // 同步塊价涝,線程安全的創(chuàng)建實(shí)例
if (instance == null) { // 再次檢查實(shí)例是否存在
instance = new SingletonLock();
}
}
}
return instance;
}
}
靜態(tài)內(nèi)部類(lèi)
推薦使用
public class SingletonHolder {
private SingletonHolder() {
}
public static SingletonHolder getInstance() {
return InstanceHolder.instance;
}
private static class InstanceHolder {
private static final SingletonHolder instance = new SingletonHolder();
}
}
枚舉
不僅能避免多線程同步問(wèn)題,而且還能防止反序列化重新創(chuàng)建新的對(duì)象持舆。1.5中才加入enum特性
public enum SingletonEnum {
INSTANCE;
public void otherMethod() {
Log.d("SingletonEnum", String.valueOf(INSTANCE));
}
}
集合管理
ConcurrentHashMap 線程安全Map色瘩,解決并發(fā)問(wèn)題
public class SingletonManager {
private static ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();
private SingletonManager() {
}
public static void putInstance(String key, Object instance) {
singletonMap.putIfAbsent(key, instance);
}
public static Object getInstance(String key) {
return singletonMap.get(key);
}
}
問(wèn)題
類(lèi)加載器
如果單例由不同的類(lèi)加載器載入,那便有可能存在多個(gè)單例類(lèi)的實(shí)例逸寓。假定不是遠(yuǎn)端存取居兆,例如一些servlet容器對(duì)每個(gè)servlet使用完全不同的類(lèi)加載器,這樣的話如果有兩個(gè)servlet訪問(wèn)一個(gè)單例類(lèi)竹伸,它們就都會(huì)有各自的實(shí)例泥栖。
解決方法:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null){
classLoader = Singleton.class.getClassLoader();
}
return classLoader.loadClass(classname);
}
序列化
如果Singleton實(shí)現(xiàn)了java.io.Serializable接口,那么這個(gè)類(lèi)的實(shí)例就可能被序列化和反序列化勋篓。不管怎樣吧享,如果你序列化一個(gè)單例類(lèi)的對(duì)象,接下來(lái)反序列化多個(gè)那個(gè)對(duì)象譬嚣,那你就會(huì)有多個(gè)單例類(lèi)的實(shí)例钢颂。
解決方法:
public class SingletonSerialize implements java.io.Serializable{
private static SingletonSerialize instance = new SingletonSerialize();
private SingletonSerialize() {
}
public static SingleSingletonSerializetonF getInstance() {
return instance;
}
// 添加此方法確保反序列化單例
private Object readResolve() throws java.io.ObjectStreamException {
return instance;
}
}