1. 程式人生 > >java多執行緒:5.1 鎖-基礎

java多執行緒:5.1 鎖-基礎

什麼是鎖

提到多執行緒,立馬就有人說加鎖,什麼是鎖,為什麼加鎖?
鎖:從字面意義,什麼東西加了鎖,那麼就只有有鑰匙的人才能使用,多執行緒中的鎖,也是這個意思。
為什麼加鎖:當單執行緒的時候,無論訪問什麼資源,都不需要考慮鎖的問題,但是當多個執行緒訪問同一個資源,就會發生很多千奇百怪的事,為了保證資源訪問時的唯一性,需要對訪問資源的執行緒進行限制,這就是鎖的由來。

示例:

public class MultiThread {
    public static void main(String[] args) {
        final String name = "xiaoming";
        new Thread(new Runnable() {
            @Override
            public void run() {
                String[] arr = name.split("");
                while(true){
                    for(int i = 0; i< arr.length;i++){
                        System.out.print(arr[i]);
                    }
                    System.out.println();
                }
            }
        },"小明").start();
        while(true){
            System.out.println("this is "+Thread.currentThread().getName());
        }

    }
}

----輸出:
this is main
ng
xiaoming
xiaomingthis is main
this is main
this is main
this is main
this is main

xiaothis is main

通過上述輸出內容,發現xiaoming的名字並不能完整的輸出,再輸出main方法,是因為存在兩個執行緒爭奪System.out導致的:執行緒A獲取System.out一頓操作,但是還沒有輸出,這是cpu切換執行緒,那麼執行緒B也獲取了System.out,然後輸出了,這樣就把部分執行緒A寫入的資料輸出了。這就是多個執行緒爭奪同一個資源,發生了奇怪的事情。

為什麼加鎖

多執行緒訪問同一個資源為什麼會產生各種未可知的問題?這是因為操作的原子性?就是本次操作無法一次性完成,例如:當前執行緒獲取System.out後,無法保證內容的輸出一次性完成,當其他執行緒獲取後,輸出的內容只有鬼知道。因此多執行緒訪問公共資源,一定要保證操作的原子性,即:本次操作必須一次完成。
為了保證操作的原子性:有兩種方法,1 加鎖,只要我獲得了鎖,必須等我完成操作後,其他執行緒才能訪問/修改,即排他性。2 jdk提供一種Atomic包,對基本資料型別和引用,進行包裝,保證操作的原子性。atomic的原理就是依賴一種cas機制,當你要更新某一個物件,需要提供該物件的原值,只要當前物件的值與原值相同時,才會進行新值更新,此處只介紹一下原理,具體的可以搜尋java Atomic瞭解底層原理和CAS的ABA問題及解決方案。

鎖加在什麼地方

訪問限制加在什麼地方?如果加在資源本身的話,對資源來說也是一種冗餘,而且使程式碼更加耦合,因此我們需要把訪問限制獨立出來,單獨使用一個物件進行訪問限制,這就是為什麼加鎖需要有一個物件。

鎖的種類

  1. 自旋鎖:讓沒有獲取到鎖的執行緒,一直迴圈直到獲取鎖,相當於獲取時間片的執行緒,如果沒有獲取到時間片,就while迴圈空判斷,要麼消耗掉時間片,要麼獲取鎖,自旋鎖有:無序自旋鎖、有序自旋鎖(CHL鎖、ticket鎖、MCS鎖)
  2. 通過方法或程式碼塊加synchronized,早期的StringBuffer、HashTable。
  3. jdk1.5之後,提供了concurrent包,裡面提供了lock類,因此就有了:讀鎖(ReadLock)、寫鎖(WriteLock)、讀寫鎖(ReadWriteLock)、可重入鎖(ReentrantLock)。
    根據不同種類的鎖,進行相關的程式碼解析。

總結

只要涉及到多執行緒訪問同一資源,就需要考慮鎖的問題。