Java併發程式設計鎖的分類-以及讀寫鎖的例項
1.可重入鎖:
1)概念:鎖具備可重入性,則稱為可重入鎖;可重入性表明了鎖的分配機制,是基於執行緒分配的,而不是基於方法呼叫分配的;
2):synchronized 和 ReentrantLock都是屬於可重入鎖
3):舉例說明
class ReentrantLockClass{ //當某個執行緒A申請到methodOne()方法的鎖,執行methodOne()方法,這時該物件被鎖住 //在methodOne()方法中呼叫了methodTwo()方法,methodTwo()也是需要申請鎖資源的 //此時如果執行緒A仍然需要申請鎖資源才能執行methodTwo()方法,則執行緒A則會無線等待下去 //因為這個物件的所資源已被執行緒A申請methodOne()方法的鎖鎖住 //但是我們在實際情況下是不會發生這種情況的,所以synchronized是可重入鎖 public synchronized void methodOne(){ //處理業務 methodTwo(); //處理業務 } public synchronized void methodTwo(){ //處理業務 } }
2.公平鎖和非公平鎖
1).公平鎖:儘可能的按照申請鎖資源的順序執行,當同時有多個執行緒等待一個鎖時,這個鎖被釋放掉,則等待鎖資源時間最長的執行緒,獲得該鎖許可權
2).非公平鎖:無法保證執行緒的執行順序是按照申請鎖資源的順序執行的,可能導致某個或者一些執行緒永遠獲取不到鎖
3).synchronized 是非公平鎖
ReentrantLock 預設是非公平鎖,即通過無參建構函式建立的 ,可以通過有參的建構函式建立公平鎖
ReentrantReadWriteLock 預設是非公平鎖,即通過無參建構函式建立的 ,可以通過有參的建構函式建立公平鎖
3.讀寫鎖ReentrantReadWriteLock
1) 通過synchronized實現讀鎖
package com.roger.juc; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriterLockMain { public synchronized void readFile() throws Exception{ long startTime = System.currentTimeMillis(); System.out.println("開始執行時間-startTime = " + startTime); for(int i=0; i < 5; i++){ Thread.sleep(20); System.out.println(Thread.currentThread().getName() + "正在讀取檔案資訊...."); } System.out.println(Thread.currentThread().getName() + "讀取完畢"); long endTime = System.currentTimeMillis(); System.out.println("結束時間-endTime = " + endTime); } public static void main(String[] args) { final ReadWriterLockMain readWriterLockMain = new ReadWriterLockMain(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { readWriterLockMain.readFile(); } catch (Exception e) { } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { readWriterLockMain.readFile(); } catch (Exception e) { } } }); thread1.start(); thread2.start(); } }
執行結果:
開始執行時間-startTime = 1542781854228 Thread-0正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-0讀取完畢 結束時間-endTime = 1542781854330 開始執行時間-startTime = 1542781854330 Thread-1正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-1讀取完畢 結束時間-endTime = 1542781854432 |
結論:通過synchronized實現讀鎖,是互斥的,只有執行緒Thread-0執行完成後,Thread-1執行緒才能執行
2)通過ReentrantReadWriteLock實現讀鎖
package com.roger.juc;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriterLockMain {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readFile(){
//新增讀鎖
lock.readLock().lock();
try {
long startTime = System.currentTimeMillis();
System.out.println("開始執行時間-startTime = " + startTime);
for(int i=0; i < 5; i++){
Thread.sleep(20);
System.out.println(Thread.currentThread().getName() + "正在讀取檔案資訊....");
}
System.out.println(Thread.currentThread().getName() + "讀取完畢");
long endTime = System.currentTimeMillis();
System.out.println("結束時間-endTime = " + endTime);
}catch (Exception e){
}finally {
//釋放讀鎖
lock.readLock().unlock();
}
}
public static void main(String[] args) {
final ReadWriterLockMain readWriterLockMain = new ReadWriterLockMain();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
readWriterLockMain.readFile();
} catch (Exception e) {
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
readWriterLockMain.readFile();
} catch (Exception e) {
}
}
});
thread1.start();
thread2.start();
}
}
執行結果:
開始執行時間-startTime = 1542782940771 開始執行時間-startTime = 1542782940772 Thread-0正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-1正在讀取檔案資訊.... Thread-0正在讀取檔案資訊.... Thread-0讀取完畢 結束時間-endTime = 1542782940871 Thread-1正在讀取檔案資訊.... Thread-1讀取完畢 結束時間-endTime = 1542782940872 |
結論:ReentrantReadWriteLock的讀鎖是可以共享的,執行效率明顯高於synchronized
3)ReentrantReadWriteLock的讀鎖和寫鎖
package com.roger.juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriterLockMain {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readFile(){
//新增讀鎖
lock.readLock().lock();
boolean readLock = lock.isWriteLocked();
if(!readLock){
System.out.println("當前為讀鎖");
}
try {
long startTime = System.currentTimeMillis();
System.out.println("開始執行時間-startTime = " + startTime);
for(int i=0; i < 5; i++){
Thread.sleep(20);
System.out.println(Thread.currentThread().getName() + "正在讀取檔案資訊....");
}
System.out.println(Thread.currentThread().getName() + "讀取完畢");
long endTime = System.currentTimeMillis();
System.out.println("結束時間-endTime = " + endTime);
}catch (Exception e){
}finally {
System.out.println("釋放讀鎖");
lock.readLock().unlock();
}
}
public void writeFile(){
//新增寫鎖
lock.writeLock().lock();
boolean writeLock = lock.isWriteLocked();
if(writeLock){
System.out.println("當前為寫鎖");
}
try {
long startTime = System.currentTimeMillis();
System.out.println("開始執行時間-startTime = " + startTime);
for(int i=0; i < 5; i++){
Thread.sleep(20);
System.out.println(Thread.currentThread().getName() + "正在寫檔案資訊....");
}
System.out.println(Thread.currentThread().getName() + "寫完畢");
long endTime = System.currentTimeMillis();
System.out.println("結束時間-endTime = " + endTime);
}catch (Exception e){
}finally {
System.out.println("釋放寫鎖");
lock.writeLock().unlock();
}
}
public static void main(String[] args) {
final ReadWriterLockMain readWriterLockMain = new ReadWriterLockMain();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
readWriterLockMain.readFile();
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
readWriterLockMain.writeFile();
}
});
}
}
執行結果:
當前為讀鎖 開始執行時間-startTime = 1542783555994 pool-1-thread-1正在讀取檔案資訊.... pool-1-thread-1正在讀取檔案資訊.... pool-1-thread-1正在讀取檔案資訊.... pool-1-thread-1正在讀取檔案資訊.... pool-1-thread-1正在讀取檔案資訊.... pool-1-thread-1讀取完畢 結束時間-endTime = 1542783556096 釋放讀鎖 當前為寫鎖 開始執行時間-startTime = 1542783556096 pool-1-thread-2正在寫檔案資訊.... pool-1-thread-2正在寫檔案資訊.... pool-1-thread-2正在寫檔案資訊.... pool-1-thread-2正在寫檔案資訊.... pool-1-thread-2正在寫檔案資訊.... pool-1-thread-2寫完畢 結束時間-endTime = 1542783556198 釋放寫鎖 |
結論:ReentrantReadWriteLock的讀鎖和寫鎖時互斥,
也就說如果一個執行緒已經佔用了物件的讀鎖,那麼其他執行緒想獲取到這個物件的寫鎖,則需要等待
佔用讀鎖的執行緒釋放讀鎖之後,才能執行寫操作;
當一個執行緒已經佔用了物件的寫鎖,那麼其他執行緒想獲取到這個物件的讀鎖或者寫鎖,則需要等待
佔用寫鎖的執行緒釋放寫鎖之後,才能執行相應的操作