1. 程式人生 > >Java併發程式設計鎖的分類-以及讀寫鎖的例項

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的讀鎖和寫鎖時互斥,

                也就說如果一個執行緒已經佔用了物件的讀鎖,那麼其他執行緒想獲取到這個物件的寫鎖,則需要等待

佔用讀鎖的執行緒釋放讀鎖之後,才能執行寫操作;

                當一個執行緒已經佔用了物件的寫鎖,那麼其他執行緒想獲取到這個物件的讀鎖或者寫鎖,則需要等待

佔用寫鎖的執行緒釋放寫鎖之後,才能執行相應的操作