如何解決線程安全問題


http://www.cnblogs.com/dolphin0520/p/3923737.html

一.什么時候會出現線程安全問題?

  在單線程中不會出現線程安全問題,而在多線程編程中,有可能會出現同時訪問同一個資源的情況,這種資源可以是各種類型的的資源:一個變量、一個對象、一個文件、一個數據庫表等,而當多個線程同時訪問同一個資源的時候,就會存在一個問題:

  由於每個線程執行的過程是不可控的,所以很可能導致最終的結果與實際上的願望相違背或者直接導致程序出錯。

  舉個簡單的例子:

  現在有兩個線程分別從網絡上讀取數據,然后插入一張數據庫表中,要求不能插入重復的數據。

  那么必然在插入數據的過程中存在兩個操作:

  1)檢查數據庫中是否存在該條數據;

  2)如果存在,則不插入;如果不存在,則插入到數據庫中。

  假如兩個線程分別用thread-1和thread-2表示,某一時刻,thread-1和thread-2都讀取到了數據X,那么可能會發生這種情況:

  thread-1去檢查數據庫中是否存在數據X,然后thread-2也接着去檢查數據庫中是否存在數據X。

  結果兩個線程檢查的結果都是數據庫中不存在數據X,那么兩個線程都分別將數據X插入數據庫表當中。

  這個就是線程安全問題,即多個線程同時訪問一個資源時,會導致程序運行結果並不是想看到的結果。

  這里面,這個資源被稱為:臨界資源(也有稱為共享資源)。

  也就是說,當多個線程同時訪問臨界資源(一個對象,對象中的屬性,一個文件,一個數據庫等)時,就可能會產生線程安全問題。

  不過,當多個線程執行一個方法,方法內部的局部變量並不是臨界資源,因為方法是在棧上執行的,而Java棧是線程私有的,因此不會產生線程安全問題。

二.如何解決線程安全問題?

基本上所有的並發模式在解決線程安全問題上,都采用“序列化訪問臨界資源”的方案,即在同一時刻,只能有一個線程訪問臨界資源,也稱同步互斥訪問。

 通常來說,是在訪問臨界資源的代碼前面加上一個鎖,當訪問完臨界資源后釋放鎖,讓其他線程繼續訪問。

  在Java中,提供了兩種方式來實現同步互斥訪問:synchronized和Lock。

  本文主要講述synchronized的使用方法,Lock的使用方法在下一篇博文中講述。

三.synchronized同步方法或者同步塊

  在了解synchronized關鍵字的使用方法之前,我們先來看一個概念:互斥鎖,顧名思義:能到達到互斥訪問目的的鎖。

  舉個簡單的例子:如果對臨界資源加上互斥鎖,當一個線程在訪問該臨界資源時,其他線程便只能等待。

  在Java中,每一個對象都擁有一個鎖標記(monitor),也稱為監視器,多線程同時訪問某個對象時,線程只有獲取了該對象的鎖才能訪問。

  在Java中,可以使用synchronized關鍵字來標記一個方法或者代碼塊,當某個線程調用該對象的synchronized方法或者訪問synchronized代碼塊時,這個線程便獲得了該對象的鎖,其他線程暫時無法訪問這個方法,只有等待這個方法執行完畢或者代碼塊執行完畢,這個線程才會釋放該對象的鎖,其他線程才能執行這個方法或者代碼塊。

  下面通過幾個簡單的例子來說明synchronized關鍵字的使用:

  1.synchronized方法

  下面這段代碼中兩個線程分別調用insertData對象插入數據:

public class Test {
 
    public static void main(String[] args)  {
        final InsertData insertData = new InsertData();
         
        new Thread() {
            public void run() {
                insertData.insert(Thread.currentThread());
            };
        }.start();
         
         
        new Thread() {
            public void run() {
                insertData.insert(Thread.currentThread());
            };
        }.start();
    }  
}
 
class InsertData {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
     
    public void insert(Thread thread){
        for(int i=0;i<5;i++){
            System.out.println(thread.getName()+"在插入數據"+i);
            arrayList.add(i);
        }
    }
}

此時程序的輸出結果為:

說明兩個線程在同時執行insert方法。

  而如果在insert方法前面加上關鍵字synchronized的話,運行結果為:

class InsertData {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
     
    public synchronized void insert(Thread thread){
        for(int i=0;i<5;i++){
            System.out.println(thread.getName()+"在插入數據"+i);
            arrayList.add(i);
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM