1. 程式人生 > >1.1 並發編程的一些概念

1.1 並發編程的一些概念

必須 順序執行 改變 通知 先後 有著 並不會 控制臺 有關

並發編程的一些概念

同步和異步

同步:

同步方法必須等到方法調用返回後,才能繼續後繼的行為。也就是說,同步方法執行時,如果沒有返回,則後面的方法是執行不到的。同步方法調用,調用過程中可能出現阻塞和等待。

比如說,java讀取控制臺輸入就是同步方法。

異步:

異步方法調用後立即返回,可以立即執行後繼的方法。異步方法的返回結果,采用通知的方式來告知調用者。異步方法調用,調用過程中不會出現阻塞和等待。

舉個例子:js的ajax方法就默認是異步調用的,執行後馬上返回,繼續執行下面的代碼。如果你想操作結果,就需要傳遞success方法。那麽異步方法調用完成後,會通知你返回結果並調用success方法。也就是說異步方法不會馬上得到結果的。

並發和並行

並發 :運行多個任務,不一定要同時,可以交替運行。
並行 :同時運行多個任務,互不幹涉。

1.1 並發編程三要素

  • 原子性

原子,是指一個操作是不可中斷的。在Java中原子性指的是一個或多個操作要麽全部執行成功要麽全部執行失敗。

  • 有序性

程序執行的順序按照代碼的先後順序執行。(處理器可能會對指令進行重排序,必須保證指令重排序不會影響多個線程執行的結果)

  • 可見性

當多個線程訪問同一個共享變量時,如果其中一個線程對這個共享變量作了修改,其他線程能知道這個修改,並獲取到最新的值。

1.2 並發級別

  • 阻塞
  • 無饑餓
  • 無障礙
  • 無鎖
  • 無等待

饑餓,阻塞和非阻塞

  • 阻塞:阻塞是指有其他線程占用了臨界區的資源,那麽當前線程必須在這個臨界區中等待,導致這個線程掛起。這個線程就是阻塞的。如果一個線程是阻塞的,那麽在其他線程釋放資源之前,當前線程無法執行。

  • 非阻塞:是指沒有一個線程會妨礙其他線程執行,線程會一直往下執行。

  • 饑餓:是指一個或多個線程因為某種原因無法獲取所需的資源,導致一直無法執行。

    例如有些線程的優先級太低,導致一直被其他高優先級的線程搶占資源執行。(競爭優先級低,無法獲取線程資源)

鎖:通過對代碼塊或方法加鎖來阻止線程進入,只有獲取到鎖的線程才能執行代碼。

1.1 對象鎖,重入鎖,顯式鎖,不可重入鎖,讀寫鎖,偏向鎖,可中斷鎖

  • 內置鎖:synchronized關鍵字。

  • 對象鎖:鎖和對象有關,而且每個對象都會有個隱形的監視器。synchronized是一種對象鎖。

  • 顯式鎖:Reentranctlock類

  • 重入鎖:線程可以多次獲得已經由它自己持有的鎖,稱之為重入鎖。

    synchronized關鍵字和Reentranctlock類都是可重入鎖。

  • 不可重入鎖:當前線程獲得鎖之後,此線程無法再次進入同步代碼,稱之為不可重入鎖。

  • 讀寫鎖:讀寫鎖將對一個資源(比如文件)的訪問分成了2個鎖,一個讀鎖和一個寫鎖。

    例如:ReentrantReadWriteLock,ReentrantReadWriteLock,分別為讀鎖與寫鎖。

  • 自旋鎖:自旋鎖是采用讓當前線程不停地的在循環體內執行實現的,當循環的條件被其他線程改變時 才能進入臨界區。

  • 互斥鎖:即兩個線程獲得鎖是互斥的,一個線程獲得了鎖,其他線程在此線程釋放鎖之前是不能獲得鎖的,java提供的一個互斥鎖為Reentrantlock。

  • 可中斷鎖:顧名思義,就是可以相應中斷的鎖。

    synchronized,不是可中斷鎖

    而Reentranctlock是可中斷鎖(例如使用lockInterruptibly方法中斷線程)。

1.2 悲觀鎖,樂觀鎖,自旋鎖,互斥鎖

  • 悲觀鎖:對數據的一致性表示悲觀。假定訪問數據時,數據會被修改,所以會發生並發沖突,每次操作都會加鎖。

    例如獨占鎖,讀寫鎖等。synchronized就是一種獨占鎖,也是是一種悲觀鎖。

  • 樂觀鎖:對數據的一致性表示樂觀。假定訪問數據時,數據不會被修改,所以不會發生並發沖突,采用錯誤重試機制。每次操作申請鎖失敗後不會立刻掛起而是稍微等待再次嘗試,如果因為發生沖突就重試,直到成功為止,以減少線程因為掛起、阻塞、喚醒(發生CPU的調度切換) 而造成的開銷。

    例如:偏向鎖,自旋鎖,CAS鎖。ReentrantLock類就是一種CAS鎖,也是一種樂觀鎖。

  • 自旋鎖:設定自旋等待的時間,等持有鎖的線程釋放鎖後即可立即獲取鎖 。超時後停止自旋進入阻塞狀態

  • 偏向鎖:偏向於第一個訪問鎖的線程 。一個線程訪問觸發偏向鎖 ,遇到了其他線程搶占鎖,則持有偏向鎖的線程會被掛起,JVM會消除它身上的偏向鎖,將鎖恢復到標準的輕量級鎖

  • CAS鎖:(比較與交換,Compare and swap) 是一種有名的無鎖算法

    當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試

    CAS有3個操作數,內存值V,舊的預期值A,新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則等待並繼續嘗試。

    說明
    • 悲觀鎖會導致阻塞,性能比較低
    • 樂觀鎖不會阻塞線程,性能比較好
    • 如果每次訪問沖突概率小於 20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次
      數不得小於 3 次。

1.3 公平鎖和非公平鎖

  • 非公平鎖:是指線程獲取鎖,采用搶占機制,線程隨機獲取鎖的。非公平鎖性能更好,通常線程鎖都是非公平鎖。

    說明
    • 例如synchronized就是非公平鎖,它無法保證等待的線程獲取鎖的順序。
    • 對於ReentrantLock和ReentrantReadWriteLock等,它默認情況下是非公平鎖,
    • 對於ReentrantLock和ReentrantReadWriteLock等,可以設置參數setFair(true)來使用公平鎖.
  • 公平鎖:是指線程獲得鎖的順序是按照線程加鎖的順序來分配的。即先來先得FIFI(先加鎖的線程先執行)。

    對於ReentrantLock和ReentrantReadWriteLock等,可以設置參數setFair(true)來使用公平鎖.

1.5 死鎖,活鎖

  • 死鎖:指兩個或兩個以上的線程分別持有鎖,並等待對方釋放鎖的現象。

    進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象。若無外力作用,死鎖將永遠得不到解開,比如2個線程互相持有對方的鎖,互相等待對方釋放鎖。那麽這2個線程都得不到釋放鎖,稱之為死鎖。(競爭時阻塞,相互等待,導致無法釋放鎖)

  • 活鎖:指的是任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致一直重復嘗試—失敗—嘗試—失敗的過程。處於活鎖的實體是在不斷的改變狀態,有著最終解開鎖的可能性。(競爭時謙讓,一直重試,導致獲取不到鎖)

    對多個資源、數據庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造
    成死鎖

    ?

1.1 並發編程的一些概念