1. 程式人生 > 實用技巧 >前方一系列大事務問題來襲,請及時拉響警報

前方一系列大事務問題來襲,請及時拉響警報

技術標籤:java多執行緒redis計算機視覺linux

該類自 JDK 8 加入,是為了進一步優化讀效能,它的特點是在使用讀鎖、寫鎖時都必須配合【戳】使用

讀鎖:

long stamp = lock.readLock();
lock.unlockRead(stamp);

寫鎖:

long stamp = lock.writeLock();
lock.unlockWrite(stamp);

StampedLock 支援tryOptimisticRead() 方法(樂觀讀),讀取完畢後需要做一次 戳校驗 如果校驗通過,表示這期間確實沒有寫操作,資料可以安全使用,如果校驗沒通過,需要重新獲取讀鎖,保證資料安全。

long stamp = lock.tryOptimisticRead();
// 驗戳
if(!lock.validate(stamp)){
 // 鎖升級
}

測試程式碼:

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.StampedLock;
import static  com.dalianpai.Sleeper.sleep;
/**
 * @author WGR
 * @create 2020/12/31 -- 13:55
 */
@Slf4j(topic = "c.TestStampedLock")
public class TestStampedLock {
    public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1);
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.read(0);
        }, "t2").start();
    }
}

@Slf4j(topic = "c.DataContainerStamped")
class DataContainerStamped {
    private int data;
    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        long stamp = lock.tryOptimisticRead();
        log.debug("optimistic read locking...{}", stamp);
        sleep(readTime);
        if (lock.validate(stamp)) {
            log.debug("read finish...{}, data:{}", stamp, data);
            return data;
        }
        // 鎖升級 - 讀鎖
        log.debug("updating to read lock... {}", stamp);
        try {
            stamp = lock.readLock();
            log.debug("read lock {}", stamp);
            sleep(readTime);
            log.debug("read finish...{}, data:{}", stamp, data);
            return data;
        } finally {
            log.debug("read unlock {}", stamp);
            lock.unlockRead(stamp);
        }
    }

    public void write(int newData) {
        long stamp = lock.writeLock();
        log.debug("write lock {}", stamp);
        try {
            sleep(2);
            this.data = newData;
        } finally {
            log.debug("write unlock {}", stamp);
            lock.unlockWrite(stamp);
        }
    }
}

測試 讀-讀 可以優化

public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(10);
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.read(0);
        }, "t2").start();
    }
}

image-20201231140233219

測試描述:當t1執行緒先搶到樂觀讀鎖,然後睡眠10秒,t2晚t1 0.5秒搶到鎖,然後直接結束,並沒有堵塞住。

測試讀-寫 時優化讀補加讀鎖

public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainer.read(1);
        }, "t1").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.write(100);
        }, "t2").start();
    }

image-20201231141057253

測試描述:當t1執行緒先搶到樂觀讀鎖,然後休眠1秒鐘,t2執行緒晚t1執行緒0.5秒搶到讀鎖,然後休眠2秒,這個時候t1執行緒醒來,驗籤失敗,這個時候就會鎖進升級成讀鎖,但是鎖還在寫鎖那邊,只能等寫鎖釋放,讀鎖才能拿到。

注意:
StampedLock不支援條件變數
StampedLock不支援可重入