1、ThreadLocal
線程局部變量,是一種多線程間并發(fā)訪問變量的解決方案,與synchronized加鎖方式不同,ThreadLocal完全不提供鎖,而使用以空間換時間的手段,為每個線程提供變量的獨立副本,保障線程安全。
從性能上講, ThreadLocal不具有絕對的優(yōu)勢,在并發(fā)不是很高的時候,加鎖性能會更好,但在高并發(fā)或者競爭激烈時,如硬件較好,用ThreadLocal一定程度上減少鎖競爭。
2、單例模式
單例模式的饑餓與懶漢模式在多線程中是不行的,性能不高且不能保證線程安全
靜態(tài)內(nèi)部類模式,推薦,最安全,最可靠
public class InnerSingleton {
private static class Singleton {
private static Singleton single = new Singleton();
}
public static Singleton getInstance() {
return Singleton.single;
}
}
比較:
package demo2;
public class MySingleTon {
// 1:餓漢 一旦完成加載,就把單例初始化完成,getInstance時已存在
private final static MySingleTon singleTon = new MySingleTon();
private MySingleTon() {
System.out.println("starting init single");
}
public static MySingleTon getInstance() {
return singleTon;
}
public static void main(String[] args) {
System.out.println("-------------");
MySingleTon sinle1 = MySingleTon.getInstance();
System.out.println("-------------");
MySingleTon sinle2 = MySingleTon.getInstance();
System.out.println("-------------");
MySingleTon sinle3 = MySingleTon.getInstance();
}
}
打?。? starting init single
-------------
-------------
-------------
package demo2;
public class MySingleTon {
// 2 線程安全的 懶漢式 調(diào)用getInstance時 初始化實例
private static MySingleTon single = null;
private MySingleTon() {
System.out.println("starting init single");
}
public static MySingleTon getInstance() {
if (single == null) {
synchronized(MySingleTon.class) {
if (single == null) {
single = new MySingleTon();
}
}
}
return single;
}
public static void main(String[] args) {
System.out.println("-------------");
MySingleTon sinle1 = MySingleTon.getInstance();
System.out.println("-------------");
MySingleTon sinle2 = MySingleTon.getInstance();
System.out.println("-------------");
MySingleTon sinle3 = MySingleTon.getInstance();
}
}
打印
-------------
starting init single
-------------
-------------
public class Singleton
{
private Singleton(){ }
public static Singleton getInstance()
{
return Nested.instance;
}
//在第一次被引用時被加載
static class Nested
{
private static Singleton instance = new Singleton();
}
public static void main(String args[])
{
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
3、至于1、2、3這三種實現(xiàn)又有些區(qū)別:
- 第1種:餓漢式在類創(chuàng)建的同時就實例化一個靜態(tài)對象出來,不管之后會不會使用這個單例,都會占據(jù)一定的內(nèi)存,但是相應的,在第一次調(diào)用時速度也會更快,因為其資源已經(jīng)初始化完成,
- 第2種,在
getInstance中做了兩次null檢查,確保了只有第一次調(diào)用單例的時候才會做同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗 - 第3種,利用了
classloader的機制來保證初始化instance時只有一個線程,所以也是線程安全的,同時沒有性能損耗,所以一般我傾向于使用這一種。
PS1
一種通過內(nèi)部類來實現(xiàn)單例的方式,靜態(tài)內(nèi)部類只能訪問外部類的靜態(tài)方法和靜態(tài)屬性?,F(xiàn)在一般利用這個特性來實現(xiàn)單例模式。因為類在初始化的時候線程是互斥的,可以完美的解決單例創(chuàng)建沖突的問題。
PS2
單例模式是一種常見的模式,懶漢模式考慮線程安全需要在獲取單例的方法添加synchronized關鍵字實現(xiàn)同步代碼塊,這樣造成了性能損耗;而餓漢模式不能延遲實例化對象,靜態(tài)內(nèi)部類單例模式的實現(xiàn),既保證了線程的安全,有能夠延遲加載,也就是在第一次使用的時候加載。