上一篇文章中,咱們已經介紹過了各類鎖,讓各位對鎖有了必定的瞭解。接下來將爲各位介紹鎖在Java中的實現。關注個人公衆號「Java面典」瞭解更多 Java 相關知識點。html
在 Java 中主要經過使用synchronized 、 volatile關鍵字,及 Lock 接口的子類 ReentrantLock 和 ReadWriteLock 等來實現加鎖。java
synchronized 屬於獨佔式的悲觀鎖,同時屬於可重入鎖。數據庫
synchronized 能夠把任意一個非 NULL 的對象看成鎖。其在不一樣場景下的做用範圍以下:安全
它有多個隊列,當多個線程一塊兒訪問某個對象監視器的時候,對象監視器會將這些線程存儲在不一樣的容器中。多線程
參考資料併發
比 sychronized 更輕量級的同步鎖ide
使用 volatile 必須同時知足下面兩個條件才能保證在併發環境的線程安全:函數
對 volatile 變量的單次讀/寫操做能夠保證原子性的,如 long 和 double 類型變量,可是並不能保證 i++ 這種操做的原子性,由於本質上 i++ 是讀、寫兩次操做。
ui
Java 中的鎖都實現於 Lock 接口,主要方法有:this
該方法和lock()的區別在於,若是鎖不可用,tryLock()不會致使當前線程被禁用。
Condition 的做用是對鎖進行更精確的控制。對於同一個鎖,咱們能夠建立多個 Condition,在不一樣的狀況下使用不一樣的 Condition。
可重入鎖。
除了能完成 synchronized 所能完成的全部工做外,還提供了諸如可響應中斷鎖、可輪詢鎖請求、定時鎖等避免多線程死鎖的方法。
public class MyLock { private Lock lock = new ReentrantLock(); // Lock lock = new ReentrantLock(true); //公平鎖 // Lock lock = new ReentrantLock(false); //非公平鎖 private Condition condition = lock.newCondition(); //建立 Condition public void testMethod() { try { lock.lock(); //lock 加鎖 // 1:wait 方法等待: //System.out.println("開始 wait"); condition.await(); // 經過建立 Condition 對象來使線程 wait,必須先執行 lock.lock 方法得到鎖 // 2:signal 方法喚醒 condition.signal(); //condition 對象的 signal 方法能夠喚醒 wait 線程 for (int i = 0; i < 5; i++) { System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1))); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
共享鎖(讀-寫鎖)
CountDownLatch 是一個同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一個或多個線程一直等待。
final CountDownLatch latch = new CountDownLatch(2); new Thread() { public void run() { System.out.println("子線程" + Thread.currentThread().getName() + "正在執行"); Thread.sleep(3000); System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢"); latch.countDown(); } ; }.start(); new Thread() { public void run() { System.out.println("子線程" + Thread.currentThread().getName() + "正在執行"); Thread.sleep(3000); System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢"); latch.countDown(); } ; }.start(); System.out.println("等待 2 個子線程執行完畢..."); latch.await(); System.out.println("2 個子線程已經執行完畢"); System.out.println("繼續執行主線程");
CyclicBarrier 是一個同步輔助類,容許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。由於該 barrier 在釋放等待線程後能夠重用,因此稱它爲循環 的 barrier。
CyclicBarrier 中最重要的方法就是 await 方法,它有 2 個重載版本:
public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for (int i = 0; i < N; i++) new Writer(barrier).start(); } static class Writer extends Thread { private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { try { Thread.sleep(5000); //以睡眠來模擬線程須要預約寫入數據操做 System.out.println("線程" + Thread.currentThread().getName() + "寫入數據完 畢,等待其餘線程寫入完畢"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("全部線程寫入完畢,繼續處理其餘任務,好比數據操做"); } }
Semaphore 是一種基於計數的信號量。它能夠設定一個閾值,基於此,多個線程競爭獲取許可信號,作完本身的申請後歸還,超過閾值後,線程申請許可信號將會被阻塞。Semaphore 能夠用來構建一些對象池,資源池之類的,好比數據庫鏈接池。
咱們也能夠建立計數爲 1 的 Semaphore,將其做爲一種相似互斥鎖的機制,這也叫二元信號量,表示兩種互斥狀態。
它的用法以下:
// 建立一個計數閾值爲 5 的信號量對象 // 只能 5 個線程同時訪問 Semaphore semp = new Semaphore(5); try { // 申請許可 semp.acquire(); try { // 業務邏輯 } catch (Exception e) { } finally { // 釋放許可 semp.release(); } } catch (InterruptedException e) { }
Semaphore 基本能完成 ReentrantLock 的全部工做,使用方法也有許多相似之處:
Java多線程併發03——什麼是線程上下文,線程是如何調度的