1. 程式人生 > >ReentrantReadWriteLock讀寫鎖的使用

ReentrantReadWriteLock讀寫鎖的使用

  類ReentrantLock具有完全互斥排他的效果,即同一時間只有一個執行緒在執行ReentrantLock.lock()後面的程式碼。這樣雖然保證了執行緒的安全性,但是效率低下。JDK提供了ReentrantReadWriteLock讀寫鎖,使用它可以加快效率,在某些不需要操作例項變數的方法中,完全可以使用讀寫鎖ReemtrantReadWriteLock來提升該方法的執行速度。

  讀寫鎖表示有兩個鎖,一個是讀操作相關的鎖,也稱為共享鎖;另一個是寫操作相關的鎖,也叫排他鎖。也就是多個讀鎖之間不互斥,讀鎖與寫鎖互斥、寫鎖與寫鎖互斥。在沒有執行緒Thread進行寫入操作時,進行讀取操作的多個Thread都可以獲取讀鎖,而進行寫入操作的Thread只有在獲取寫鎖後才能進行寫入操作。即多個Thread可以同時進行讀取操作,但是同一時刻只允許一個Thread進行寫入操作。

1.讀讀共享

  讀鎖與讀鎖可以共享,這種鎖一般用於只讀操作,不對變數進行修改操作。

package cn.qlq.thread.twelve;

import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.qlq.thread.one.RunnableThread;

public class Demo1 {
    private ReentrantReadWriteLock lock = new
ReentrantReadWriteLock();// 讀寫鎖 private static final Logger log = LoggerFactory.getLogger(Demo1.class); private int i; public String readI() { try { lock.readLock().lock();// 佔用讀鎖 log.info("threadName -> {} 佔用讀鎖,i->{}", Thread.currentThread().getName(), i); Thread.sleep(
2 * 1000); } catch (InterruptedException e) { } finally { log.info("threadName -> {} 釋放讀鎖,i->{}", Thread.currentThread().getName(), i); lock.readLock().unlock();// 釋放讀鎖 } return i + ""; } public static void main(String[] args) { final Demo1 demo1 = new Demo1(); Runnable runnable = new Runnable() { @Override public void run() { demo1.readI(); } }; new Thread(runnable, "t1").start(); new Thread(runnable, "t2").start(); new Thread(runnable, "t3").start(); } }

結果:

18:27:20 [cn.qlq.thread.twelve.Demo1]-[INFO] threadName -> t2 佔用讀鎖,i->0
18:27:20 [cn.qlq.thread.twelve.Demo1]-[INFO] threadName -> t1 佔用讀鎖,i->0
18:27:20 [cn.qlq.thread.twelve.Demo1]-[INFO] threadName -> t3 佔用讀鎖,i->0
18:27:22 [cn.qlq.thread.twelve.Demo1]-[INFO] threadName -> t3 釋放讀鎖,i->0
18:27:22 [cn.qlq.thread.twelve.Demo1]-[INFO] threadName -> t1 釋放讀鎖,i->0
18:27:22 [cn.qlq.thread.twelve.Demo1]-[INFO] threadName -> t2 釋放讀鎖,i->0

 

2.寫寫互斥

  寫鎖與寫鎖互斥,這就類似於ReentrantLock的作用效果。

package cn.qlq.thread.twelve;

import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo2 {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();// 讀寫鎖
    private static final Logger log = LoggerFactory.getLogger(Demo2.class);

    private int i;

    public void addI() {
        try {
            lock.writeLock().lock();// 佔用寫鎖
            log.info("threadName -> {} 佔用寫鎖,i->{}", Thread.currentThread().getName(), i);
            Thread.sleep(2 * 1000);
            i++;
        } catch (InterruptedException e) {

        } finally {
            log.info("threadName -> {} 釋放寫鎖,i->{}", Thread.currentThread().getName(), i);
            lock.writeLock().unlock();// 釋放寫鎖
        }
    }

    public static void main(String[] args) {
        final Demo2 demo1 = new Demo2();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                demo1.addI();
            }
        };

        new Thread(runnable, "t1").start();
        new Thread(runnable, "t2").start();
        new Thread(runnable, "t3").start();

    }

}

結果:(從時間可以看出實現了互斥效果)

18:31:31 [cn.qlq.thread.twelve.Demo2]-[INFO] threadName -> t1 佔用寫鎖,i->0
18:31:33 [cn.qlq.thread.twelve.Demo2]-[INFO] threadName -> t1 釋放寫鎖,i->1
18:31:33 [cn.qlq.thread.twelve.Demo2]-[INFO] threadName -> t2 佔用寫鎖,i->1
18:31:35 [cn.qlq.thread.twelve.Demo2]-[INFO] threadName -> t2 釋放寫鎖,i->2
18:31:35 [cn.qlq.thread.twelve.Demo2]-[INFO] threadName -> t3 佔用寫鎖,i->2
18:31:37 [cn.qlq.thread.twelve.Demo2]-[INFO] threadName -> t3 釋放寫鎖,i->3

 

3.讀寫互斥

package cn.qlq.thread.twelve;

import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 讀寫互斥
 * 
 * @author Administrator
 *
 */
public class Demo3 {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();// 讀寫鎖
    private static final Logger log = LoggerFactory.getLogger(Demo3.class);

    private int i;

    public String readI() {
        try {
            lock.readLock().lock();// 佔用讀鎖
            log.info("threadName -> {} 佔用讀鎖,i->{}", Thread.currentThread().getName(), i);
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {

        } finally {
            log.info("threadName -> {} 釋放讀鎖,i->{}", Thread.currentThread().getName(), i);
            lock.readLock().unlock();// 釋放讀鎖
        }
        return i + "";
    }

    public void addI() {
        try {
            lock.writeLock().lock();// 佔用寫鎖
            log.info("threadName -> {} 佔用寫鎖,i->{}", Thread.currentThread().getName(), i);
            Thread.sleep(2 * 1000);
            i++;
        } catch (InterruptedException e) {

        } finally {
            log.info("threadName -> {} 釋放寫鎖,i->{}", Thread.currentThread().getName(), i);
            lock.writeLock().unlock();// 釋放寫鎖
        }
    }

    public static void main(String[] args) throws InterruptedException {
        final Demo3 demo1 = new Demo3();

        new Thread(new Runnable() {
            @Override
            public void run() {
                demo1.readI();
            }
        }, "t1").start();

        Thread.sleep(1 * 1000);

        new Thread(new Runnable() {
            @Override
            public void run() {
                demo1.addI();
            }
        }, "t2").start();

    }

}

結果:

18:34:59 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t1 佔用讀鎖,i->0
18:35:01 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t1 釋放讀鎖,i->0
18:35:01 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t2 佔用寫鎖,i->0
18:35:03 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t2 釋放寫鎖,i->1

 

4.寫讀互斥

  寫鎖與讀鎖也是互斥的。先佔用寫鎖後讀鎖進行搶佔也會等待寫鎖釋放。

package cn.qlq.thread.twelve;

import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 讀寫互斥
 * 
 * @author Administrator
 *
 */
public class Demo3 {
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();// 讀寫鎖
    private static final Logger log = LoggerFactory.getLogger(Demo3.class);

    private int i;

    public String readI() {
        try {
            lock.readLock().lock();// 佔用讀鎖
            log.info("threadName -> {} 佔用讀鎖,i->{}", Thread.currentThread().getName(), i);
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {

        } finally {
            log.info("threadName -> {} 釋放讀鎖,i->{}", Thread.currentThread().getName(), i);
            lock.readLock().unlock();// 釋放讀鎖
        }
        return i + "";
    }

    public void addI() {
        try {
            lock.writeLock().lock();// 佔用寫鎖
            log.info("threadName -> {} 佔用寫鎖,i->{}", Thread.currentThread().getName(), i);
            Thread.sleep(2 * 1000);
            i++;
        } catch (InterruptedException e) {

        } finally {
            log.info("threadName -> {} 釋放寫鎖,i->{}", Thread.currentThread().getName(), i);
            lock.writeLock().unlock();// 釋放寫鎖
        }
    }

    public static void main(String[] args) throws InterruptedException {
        final Demo3 demo1 = new Demo3();

        new Thread(new Runnable() {
            @Override
            public void run() {
                demo1.addI();
            }
        }, "t2").start();

        Thread.sleep(1 * 1000);

        new Thread(new Runnable() {
            @Override
            public void run() {
                demo1.readI();
            }
        }, "t1").start();
    }

}

結果:

18:36:14 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t2 佔用寫鎖,i->0
18:36:16 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t2 釋放寫鎖,i->1
18:36:16 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t1 佔用讀鎖,i->1
18:36:18 [cn.qlq.thread.twelve.Demo3]-[INFO] threadName -> t1 釋放讀鎖,i->1

 

總結: 讀寫、寫讀、寫寫都是互斥的,而讀讀是非同步非互斥的。

      也就是隻要有寫鎖的參與就會進行同步,所以寫鎖也被稱為排他鎖,讀鎖被稱為共享鎖。