線程同步,對象級別鎖,類級別鎖

同步一般是指在多線程中,在某一時刻,一個同步塊代碼只能在一個線程中執行。

Java支持多線程執行,所以可能出現兩個或者更多的線程訪問同一個字段或者對象。同步是一個過程,保持所有併發線程同步執行。同步避免了因共享內存不一致的問題而引起的內存一致性錯誤。當一個方法聲明爲同步,並有一個線程持有這個方法對象的監視器,你的線程就會被阻塞,直到這個線程釋放這個監視器。

同步在Java中使用synchronized 這個關鍵字來實現,你可以使用這個關鍵字在你定義類的方法或者代碼塊中,但是這個關鍵字不能使用在類定義的變量或者屬性中。

對象級別鎖

對象級別鎖是一個機制,當你想同步一個非靜態方法或者非靜態代碼塊,讓在給定的類實例中只有一個線程來執行這個代碼塊,這就可以使得實例級別的數據是線程安全的。具體做法如下:

public class DemoClass
{
    public synchronized void demoMethod(){}
}

or

public class DemoClass
{
    public void demoMethod(){
        synchronized (this)
        {
            //other thread safe code
        }
    }
}

or

public class DemoClass
{
    private final Object lock = new Object();
    public void demoMethod(){
        synchronized (lock)
        {
            //other thread safe code
        }
    }
}

類級別鎖

在所有可變的實例或者運行環境中,類級別鎖阻止多線程進入同步塊,也就是說,如果運行環境中有DemoClass的100個實例,在任何時刻,只能有DemoClass的一個實例來執行它的demoMethod()方法,所有其他的DemoClass實例在其他線程中只能處於阻塞狀態,這使得靜態數據是線程安全的

public class DemoClass
{
    public synchronized static void demoMethod(){}
}

or

public class DemoClass
{
    public void demoMethod(){
        synchronized (DemoClass.class)
        {
            //other thread safe code
        }
    }
}

or

public class DemoClass
{
    private final static Object lock = new Object();
    public void demoMethod(){
        synchronized (lock)
        {
            //other thread safe code
        }
    }
}

一些重要點

1、在Java中,同步保證兩個線程不能同時執行同一個同步方法需要有相同的同步或者併發鎖。

2、synchronized關鍵字只能用在方法或者代碼塊中,這些方法或者代碼塊可以使靜態的也可以使非靜態的。

3、當一個線程進入到java的synchronized 方法或者塊中的時候,它會獲取一個鎖,並且無論什麼時候它離開這個javasynchronized方法或者塊的時候,它會釋放這個鎖。當執行完成離開synchronized塊或者出現任何錯誤以及異常的時候都會釋放鎖。

4、java的synchronized 關鍵字本質上是可重入的,這意味着如果java同步方法調用到了另一個同步方法,並且這個方法需要同樣的鎖,那麼持有鎖的這個方法是可以進入另一個需要同一個鎖的方法的。

例如:

class MyClass {
    public synchronized void method1() {
        method2();
    }

    public synchronized void method2() {

    }
}

上述代碼中的兩個方法method1和method2都用synchronized修飾了,假如某一時刻,線程A執行到了method1,此時線程A獲取了這個對象的鎖,而由於method2也是synchronized方法,假如synchronized不具備可重入性,此時線程A需要重新申請鎖。但是這就會造成一個問題,因爲線程A已經持有了該對象的鎖,而又在申請獲取該對象的鎖,這樣就會線程A一直等待永遠不會獲取到的鎖。

  而由於synchronized和Lock都具備可重入性,所以不會發生上述現象。

5、如果用在同步塊中的對象是空,那麼java同步將會拋出NullPointerException。

例如:下面就會拋出空指針異常

public class DemoClass
{
    private final static Object lock = null;
    public void demoMethod(){
        synchronized (lock)
        {
        }
    }
}

6、在Java中,方法的同步會影響應用的性能,所以在完全需要的時候才使用這個同步,另外,可以考慮使用同步代碼塊來同步你需要同步的關鍵代碼。

7、靜態同步方法和非靜態同步方法是可能同時運行的,因爲它們使用的是不同的對象鎖。

8、根據java的語言規範,不能在構造函數中使用java的synchronized關鍵字,它是非法的並會導致編譯錯誤。

9、在java中,不能在同步塊中同步非final字段,因爲,非final字段的引用可能在任何時候發生改變,這樣如果不同的線程同步不用的對象就相當於沒有同步。最好使用String類,它已經聲明爲終態並且是不可變的。

發佈了343 篇原創文章 · 獲贊 120 · 訪問量 68萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章