簡體   English   中英

如何通過Java並發鎖實現此類用例

[英]How to implement such use case by Java Concurrency Lock

在下面的測試用例中, update()方法在類外部調用,該類每小時僅由一個線程運行一次。 但是有多個線程同時調用getKey1()getKey2()

因此,對於此用例:

最重要的是,由於getKey1()getKey2()幾乎同時調用,因此我們必須確保

  1. 如果flag為true,則更新key1和key2並返回新密鑰
  2. 如果不是,請獲取舊的key1和key2

我們不希望出現這種情況:更新key1並獲取舊的key2,但是即使有一些請求,即使我們已經更新了它,也可以獲取舊的key1和key2。

public class Test {

    private Lock lock = new ReentrantLock();

    private AtomicBoolean flag1 = new AtomicBoolean(false);
    private AtomicBoolean flag2 = new AtomicBoolean(false);

    private volatile String key1;
    private volatile String key2;

    public Test(String key1, String key2) {
        this.key1 = key1;
        this.key2 = key2;
    }

    public void update() {
        lock.lock();
        try {
            flag1.compareAndSet(false, true);
            flag2.compareAndSet(false, true);
        } finally {
            lock.unlock();
        }
    }

    public String getKey1() {
        // TODO if the lock is holding... block over here
        if (flag1.get()) {
            // doing something for key 1
            key1 = getFromFile();
            flag1.set(false);
        }
        return key1;
    }

    public String getKey2() {
        // TODO if the lock is holding... block over here
        if (flag2.get()) {
            // doing something for key 1
            key2 = getFromFile();
            flag2.set(false);
        }
        return key1;
    }
}

我的想法是:

  1. 當update()運行時,同時getKey1()getKey2() ,等待獲取更新密鑰
  2. 當update()未運行時, getKey1()getKey2()應該都可以正常運行並直接返回。
  3. 當調用getKey1()或getKey2()時,我認為我們不需要阻止update()方法。

有人對實現有任何想法嗎?

如前所述, java.util.concurrent.locks.ReentrantReadWriteLock最適合您。

放在您的示例中:

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Test {

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    private AtomicBoolean flag1 = new AtomicBoolean(false);
    private AtomicBoolean flag2 = new AtomicBoolean(false);

    private volatile String key1;
    private volatile String key2;

    public Test(String key1, String key2) {
        this.key1 = key1;
        this.key2 = key2;
    }

    public void update() {
        Lock writeLock = lock.writeLock();
        try {
            flag1.compareAndSet(false, true);
            flag2.compareAndSet(false, true);
        } finally {
            writeLock.unlock();
        }
    }

    public String getKey1() {
        Lock readLock = lock.readLock();
        try {
            if (flag1.get()) {
                // doing something for key 1
                key1 = getFromFile();
                flag1.set(false);
            }
            return key1;
        } finally {
            readLock.unlock();
        }
    }

    public String getKey2() {
        Lock readLock = lock.readLock();
        try {
            if (flag2.get()) {
                // doing something for key 1
                key2 = getFromFile();
                flag2.set(false);
            }
            return key1;
        } finally {
            readLock.unlock();
        }
    }
}

ReadWriteLock接口的文檔中:

只要沒有寫程序,讀鎖就可以同時由多個讀程序線程保持。 寫鎖是排他的。

下面的ReadWriteLock示例的更新,我們必須在開始讀/寫操作之前獲取鎖。 由於我無權編輯示例,因此發布了新答案。

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Test {

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    private AtomicBoolean flag1 = new AtomicBoolean(false);
    private AtomicBoolean flag2 = new AtomicBoolean(false);

    private volatile String key1;
    private volatile String key2;

    public Test(String key1, String key2) {
        this.key1 = key1;
        this.key2 = key2;
    }

    public void update() {
        Lock writeLock = lock.writeLock();
        try {
            writeLock.lock(); 
            flag1.compareAndSet(false, true);
            flag2.compareAndSet(false, true);
        } finally {
            writeLock.unlock();
        }
    }

    public String getKey1() {
        Lock readLock = lock.readLock();
        try {
            readLock.lock(); 
            if (flag1.get()) {
                // doing something for key 1
                key1 = getFromFile();
                flag1.set(false);
            }
            return key1;
        } finally {
            readLock.unlock();
        }
    }

    public String getKey2() {
        Lock readLock = lock.readLock();
        try {
            readLock.lock();
            if (flag2.get()) {
                // doing something for key 1
                key2 = getFromFile();
                flag2.set(false);
            }
            return key1;
        } finally {
            readLock.unlock();
        }
    }
}

當然,在這種情況下, ReadWriteLock應該可以提供幫助。 許多並發的讀取器可以在很小的爭用下進行操作,而寫入器很少會獲得寫入鎖定和更新。

但是,提出的密鑰問題是線程不得在一次通過中讀取一個舊密鑰和一個新密鑰。

這是一個經典的問題。 最簡單的解決方案是更新更新線程中的兩個鍵。

ReentrantReadWriteLock rwlock=new ReentrantReadWriteLock(true);
//If in doubt require a fair lock to avoid live-locking writes...

//...

public void update() {
    String new1=getFromFile();
    String new2=getFromFile();
    //That's the heavy lifting. Only now acquire the lock...
    Lock wlock=rwlock.writeLock();
    wlock.lock();
    try {
        key1=new1;
        key2=new2;
    } finally {
        wlock.unlock();
    }
}

在讀者中:

public String[] getKeys() {
    String k1;
    String k2;
    Lock rlock=rwlock.readLock();
    rlock.lock();
    try {
        k1=key1;
        k2=key2;
    } finally {
        rlock.unlock();
    }    
    String[] r={k1,k2};
    return r;
 }

讀寫鎖具有兩個連接的鎖。 多個讀取器可以獲得讀取鎖定,但是寫入鎖定不包括讀取器和其他寫入器。 這是常見的情況,並且已經建立了解決方案。

獲取文件之前獲取文件可能很重要,因為I / O通常相對較慢,因此您應將鎖定保持最短的時間。

從同一次獲取鎖中返回兩個鑰匙也很重要。 沒有其他簡單的方法可以停止在調用getKey1()getKey2()之間進行更新。

由於鎖的內存屏障保證,因此您不再需要使用volatile關鍵字(請參閱文檔)。

實際上,您實際上不需要重入鎖,而這是讀寫鎖開箱即用提供的。

在這種情況下,稱為順序鎖(Seqlock)的方法可能會有所幫助,但會涉及更多的編碼,因此可能在此處進行了過度設計。

參考文獻:

https://zh.wikipedia.org/wiki/Seqlock

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html

使用tryLock()

public String getKey1() {
  if (lock.tryLock())
  {
    // Got the lock
    try
    {
        // Process record
    }
    finally
    {
        // Make sure to unlock so that we don't cause a deadlock
        lock.unlock();
    }
 }
}

如果您不希望getKey1()或getKey2()執行鎖定。 您可以使用以下方法,

static boolean isLocked=false;
public void update() {
        lock.lock();
        isLocked = true;
        try {
            flag1.compareAndSet(false, true);
            flag2.compareAndSet(false, true);
        } finally {
            lock.unlock();
            isLocked = false; 
        }
    }

    public String getKey1() {
        while(isLocked); #wait till the lock release.
        if (flag1.get()) {
            // doing something for key 1
            key1 = getFromFile();
            flag1.set(false);
        }
        return key1;
    }

參考: 如何確定對象是否被鎖定同步所以不阻塞Java

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM