Android多執行緒研究(9)——執行緒鎖Lock
阿新 • • 發佈:2019-02-05
在前面我們在解決執行緒同步問題的時候使用了synchronized關鍵字,今天我們來看看Java 5.0以後提供的執行緒鎖Lock.
Lock介面的實現類提供了比使用synchronized關鍵字更加靈活和廣泛的鎖定物件操作,而且是以面向物件的方式進行物件加鎖。
@Override public void run() { while(true){ Lock lock = new ReentrantLock(); try { lock.lock(); Thread.sleep(new Random().nextInt(3000)); String data = readData(); System.out.print("讀取資料: " + data); } catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock(); } } }
讀寫鎖:分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥,這是由JVM控制的。
import java.util.Random; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockTest { static ReadWriteLock rwl = new ReentrantReadWriteLock(); private static String data = null; public static void main(String[] args) { Runnable runnable1 = new MyRunnable1(); Runnable runnable2 = new MyRunnable2(); for(int i=0; i<3; i++){ new Thread(runnable1).start(); new Thread(runnable2).start(); } } static class MyRunnable1 implements Runnable{ @Override public void run() { writeData("" + new Random().nextInt(100)); } } static class MyRunnable2 implements Runnable{ @Override public void run() { readData(); } } private static void writeData(String var){ rwl.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 準備寫"); Thread.sleep(new Random().nextInt(3000)); data = var; System.out.println(Thread.currentThread().getName() + " 寫完畢"); } catch (InterruptedException e) { e.printStackTrace(); }finally{ rwl.writeLock().unlock(); } } private static void readData(){ rwl.readLock().lock(); //用讀鎖鎖住 try { System.out.println(Thread.currentThread().getName() + " 準備讀"); Thread.sleep(new Random().nextInt(3000)); System.out.println(Thread.currentThread().getName() + " 讀完畢"); } catch (InterruptedException e) { e.printStackTrace(); }finally{ rwl.readLock().unlock(); } } }
用過Hibernate框架的朋友可能知道,Hibernate查詢資料庫有快取機制,如果某個資料在記憶體中存在則可以併發的去讀取,如果快取中沒有資料則需要互斥的去從資料庫取資料。
上面獲取資料的大概過程如下:import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { private Map<String, Object> cache = new HashMap<String, Object>(); public static void main(String[] args) { } private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); /** * 實現多個併發讀,互斥的寫 * @param key * @return */ public Object getData(String key){ rwl.readLock().lock(); Object value = null; try{ value = cache.get(key); if(value == null){ rwl.readLock().unlock(); //釋放讀鎖 rwl.writeLock().lock(); //新增寫鎖 try{ if(value == null){ //放置其他執行緒載入資料 value = "去資料庫查詢"; //這裡模擬從資料庫查詢 if(value == null){ //TODO 丟擲異常 } } }finally{ rwl.writeLock().unlock(); } rwl.readLock().lock(); //鎖還給讀執行緒 } }finally{ rwl.readLock().unlock(); } return value; } }
1、獲取讀鎖,讀取資料
2、如果有資料則直接返回,並釋放讀鎖讓其他執行緒讀。
3、如果記憶體中沒有資料則從資料庫寫入記憶體,釋放讀鎖並新增寫鎖(這樣寫入資料就可以達到可以互斥)
4、讀入記憶體後釋放寫鎖並還回讀鎖(和後面的unlock()對應)
5、如果在新增寫鎖的時候同時有多個執行緒,則只能有其中一個執行緒搶到鎖,等擁有鎖的執行緒釋放寫鎖後,其他執行緒就會搶到寫鎖,但是此時資料已經寫入記憶體,則需要判斷記憶體資料是否為null如果不為null則直接釋放寫鎖。