鎖和多線程:5種鎖介紹(三)


 

 

搞明白 線程 多線程系列

1.重入鎖

在類中有 synchronized 方法A 和 synchronized 方法B,並在A中調用B,就形成了重入鎖.獲得A的對象鎖,可以直接調用B,並不需要重新請求鎖權限.

package thread;

/** * @Author lyf * @Date 2018/11/18 0018 14:30 */
public class RepeatLock {
  
  public synchronized void A(){
    System.out.println("進入A...");
    B();
  }
  
  public synchronized void B(){
    System.out.println("進入B...");
  }
  
  public static void main(String[] args){
    RepeatLock repeatLock = new RepeatLock();
    for (int i = 0; i < 5; i++) {
      new Thread(()->{repeatLock.A();}).start();
    }
  }
}

 


 

 

2.中斷鎖

使用synchronized修飾的方法,在阻塞狀態blocked時不能被外部打斷,除非jvm報錯.
使用ReentrantLock中的lockInterruptibly()處於阻塞狀態時, 可以被打斷.

package thread;

import java.util.concurrent.locks.ReentrantLock;

/** * @Author lyf * @Date 2018/11/18 0018 14:47 */
public class InterruptLock {

  private ReentrantLock lock = new ReentrantLock();

  public synchronized void A() {
       try {
    System.out.println(Thread.currentThread().getName() + "獲取鎖...");
      while (true) {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName() + "執行...");
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) throws InterruptedException {
    
    InterruptLock lock1 = new InterruptLock();
    Thread t1 = new Thread(()->{lock1.A();});
    t1.start();
    Thread t2 = new Thread(()->{lock1.A();});
    t2.start();
    Thread.sleep(2000);
    System.out.println(t1.getName() + ": "+t1.getState());// RUNNABLE 正在運行
    System.out.println(t2.getName() + ": "+t2.getState());// BLOCKED 阻塞狀態
    Thread.sleep(1000);
    t2.interrupt();// 打斷t2線程
    System.out.println(t2.getName() + ": "+t2.getState());// 依然處於BLOCKED 阻塞狀態
  }
}

 


 

線程0獲取鎖后, 線程1進入阻塞等待狀態.對線程1調用interrupt()打斷線程,發現沒什么用!線程1還是在等待.

 

package thread;

import java.util.concurrent.locks.ReentrantLock;

/** * @Author lyf * @Date 2018/11/18 0018 14:47 */
public class InterruptLock {

  private ReentrantLock lock = new ReentrantLock();


  public void B() {
    try {
      lock.lockInterruptibly();
      System.out.println(Thread.currentThread().getName() + "獲取鎖...");
      while (true) {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName() + "執行...");
      }
    } catch (InterruptedException e) {
      System.out.println(Thread.currentThread().getName() + "被打斷...");
    } finally {
      lock.unlock();
    }
  }

  public static void main(String[] args) throws InterruptedException {
    
    InterruptLock lock2 = new InterruptLock();
    Thread t1 = new Thread(() -> { lock2.B(); });
    t1.start();
    Thread t2 = new Thread(() -> { lock2.B(); });
    t2.start();
    Thread.sleep(2000);
    System.out.println(t1.getName() + ": "+t1.getState());// RUNNABLE 正在運行
    System.out.println(t2.getName() + ": "+t2.getState());// WAITING 等待狀態
    Thread.sleep(1000);
    t2.interrupt();// 打斷t2線程
    System.out.println(t2.getName() + ": "+t2.getState());// WAITING 狀態(線程延時)
  }
}

 


 

很明顯線程1被打斷...

 

3.公平鎖

當多個線程處於阻塞狀態時,由系統隨機調度其中一個線程繼續執行,無法保證公平性.有可能某個線程永遠無法執行.ReentrantLock(true)可以創建公平鎖,保證每個阻塞的線程都可以被執行到.

首先,來看非公平鎖的情況,使用new ReentrantLock()創建:

package thread;

import java.util.concurrent.locks.ReentrantLock;

/** * @Author lyf * @Date 2018/11/18 0018 18:46 */
public class FairLock {

  private ReentrantLock lock = new ReentrantLock();

  public synchronized void A() {
    lock.lock();
    System.out.println(Thread.currentThread().getName() + "獲得鎖...");
    try {
      Thread.sleep(5000);
      System.out.println(Thread.currentThread().getName() + "執行...");
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      lock.unlock();
    }
  }

  public static void main(String[] args) throws InterruptedException {
    FairLock fairLock = new FairLock();
    new Thread(() -> {
      fairLock.A();
    }).start();// 啟動線程獲得鎖

    for (int i = 0; i < 5; i++) {
      new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + "等待獲得鎖...");
        fairLock.A();
      }).start();
    }
  }
}

 


 

線程0獲得鎖,線程 4 3 5 2 1 依次進入等待狀態,在線程0執行完后,隨機選擇一個線程執行.

 

使用new ReentrantLock(true)創建公平鎖

private ReentrantLock lock = new ReentrantLock(true);

 


 

線程0獲得鎖, 線程 1 2 5 4 3 依次進入等待狀態,在線程0執行完后,按照 順序 3 4 5 2 1 執行.

 

4.讀寫鎖

多個線程執行讀操作時,互不影響.但是讀寫互斥,寫寫互斥.

package thread;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/** * @Author lyf * @Date 2018/11/18 0018 10:30 */
public class MyReadWriteLock {

  private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  private int num = 0;

  public void readFile() {
    rwl.readLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + "執行讀操作...");
      Thread.sleep((long) (Math.random() * 1000));
      System.out.println(Thread.currentThread().getName() + "讀操作完成...");
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      rwl.readLock().unlock();
    }
  }


  public void writeFile() {
    rwl.writeLock().lock();
    System.out.println(Thread.currentThread().getName() + "執行寫操作...");
    try {
      num++;
      Thread.sleep((long) (Math.random() * 1000));
      System.out.println(Thread.currentThread().getName() + "寫操作完成...num: " + num);
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      rwl.writeLock().unlock();
    }

  }

  public static void main(String[] args) {

    MyReadWriteLock myReadWriteLock = new MyReadWriteLock();
    for (int i = 0; i < 5; i++) {
      new Thread(()->{myReadWriteLock.readFile();}).start();// 讀語句1
      new Thread(()->{myReadWriteLock.readFile();}).start();// 讀語句2
      new Thread(() -> { myReadWriteLock.writeFile(); }).start();// 寫語句
    }
  }
}

 


 

 

  • 線程0 1 3讀操作同時開始,與線程5寫操作互斥
  • 線程5寫操作與線程2寫操作互斥

5.自旋鎖

多線程下會造成線程處於阻塞狀態,不能執行其他操作.自旋鎖是一種非阻塞鎖,允許線程等待期間執行其他操作,直到獲得鎖.

package thread;

import java.util.concurrent.atomic.AtomicReference;

/** * @Author lyf * @Date 2018/11/18 0018 20:23 */
public class SpinLock {
  // CAS操作
  AtomicReference<Thread> runThread = new AtomicReference<>();

  public void lock() {
    Thread cur = Thread.currentThread();// 獲取當前線程
    // 如果需要運行線程標志為null,也就是說現在沒有線程在執行,就執行當前線程
    // 如果需要運行線程標志不為null,說明現在有線程在執行,就自己個兒執行自己的
    while (!runThread.compareAndSet(null, cur)) {
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + "在自旋中...");
    }
    try {
      System.out.println(Thread.currentThread().getName() + "開始執行...");
      Thread.sleep(2000);
      System.out.println(Thread.currentThread().getName() + "執行完畢...");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  public void unlock() {
    Thread cur = Thread.currentThread();
    runThread.compareAndSet(cur, null);
  }
  
  public static void main(String[] args) {
      SpinLock spinLock = new SpinLock();
      Thread t1 = new Thread(()->{
        spinLock.lock();
        try {
          Thread.sleep(2048);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        spinLock.unlock();
      });
      Thread t2 = new Thread(()->spinLock.lock());
      t1.start();
      t2.start();
  }
}

 


 

 

參考資料


免責聲明!

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



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