程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 深入理解java虛擬機(7)---線程安全 & 鎖優化,深入理解虛擬機

深入理解java虛擬機(7)---線程安全 & 鎖優化,深入理解虛擬機

編輯:JAVA綜合教程

深入理解java虛擬機(7)---線程安全 & 鎖優化,深入理解虛擬機


關於線程安全的話題,足可以使用一本書來講解這些東西。<Java Concurrency in Practice> 就是講解這些的,在這裡

主要還是分析JVM中關於線程安全這塊的內容。

1.線程安全是什麼?

線程安全,有經驗的開發人員都聽過這個名詞,但是能否給到一個准確的定義,很難。

在 Java Concurrency in Practice裡面定義是:

當多個線程訪問一個對象時,如果不用考慮這些線程在運行時的環境下的調度和交替執行,

也不需要進行額外的同步,或者調用其他協作,這個情況下,線程就是安全的。

java中的線程安全可以定義5個級別:

1)不可改變。

也就是final修飾的詞。

public class ThreadSafeType {


    class ThreadContext{
        public  int id = -1;
    }

    public void doSomeThingBackground(ThreadContext context)
    {
        new Thread(){
            @Override
            public void run(){
                context.id++;
            }
        }.start();
    }
}

上面標紅的,會再android studio裡面提示錯誤:

從內部類中訪問本地變量context; 需要被聲明為最終類型。

也就是說在

public void doSomeThingBackground(final ThreadContext context)

要改成上面的類型,這就是線程安全的考慮。

2)絕對安全

絕對安全其實很難描述,比如Vector是安全的。但是在多線程的情況下,它也是不安全的。

3)相對安全

相對安全其實就是我們一般意義上的線程安全。

它需要保證對這個對象的單獨操作是安全的。但是對於特定的順序,需要一些方法保證線程安全。

4)線程兼容

這就是我們常見的情況,需要使用synchronized等手段來保證線程安全。

5)線程對立

比較極端的情況,就是無論怎麼加鎖,代碼無法並發運行。一種情況就是死鎖。

2.如何實現線程安全

1)互斥同步

保持共享數據在同一時刻只被一個線程使用。

互斥是手段,同步是目的。

在java中最常見的就是synchronized方法。

synchronized標記的代碼,會生成monitorenter & monitorexit  2段代碼。

這是java編譯器自動生成的,不會有遺漏。使用其他鎖,lock & unlock成對出現,但是

開發者有時候會容易疏忽這個操作,尤其在catch代碼裡面忘記調用unlock,將是一個隱患。

java.util.concurrent 下面有不少同步的方法。ReentrantLock也是一個可以的方法,在1.5以前,性能

遠由於synchronized。但是在1.6, java還是把synchronized做了很大的提升。原因就是synchronized使用的

代碼已經遠遠大於ReentrantLock,並且引入ReentrantLock,可能會令需要開發者混淆。所以ReentrantLock可以認為是

一道開胃小菜而已。

2)非阻塞同步

互斥同步是一種阻塞同步,但是有些情況下,我們不需要互斥,只要能夠同步就可以。

java.util.concurrent.atomic.AtomicInteger

就是這樣一個自增的方法。

    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

源碼裡面沒有 互斥的操作,就是一直在循環,知道+1滿足就退出。

者就是非阻塞同步。

3)無同步

同步只是保證共享數據的手段,如果2個線程沒有共享數據,也就不需要同步。

3.鎖優化

1)自旋鎖

自旋鎖有時候會白白的耗用處理器的資源,但是沒有任何實際效果。

2)鎖消除

如果代碼不可能存在共享數據需要同步,編譯器就會把鎖拿掉

3)鎖粗化

原則上鎖的互斥模塊盡可能的小,但是如果對於同一對象,反復的lock & unlock 尤其是循環體中。

會帶來很大的性能損失。

 

參考:

《深入理解Java虛擬機》周志明

《Java Concurrency in Pratice》

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved