在Java中实现资源读/写锁
我试图为多个线程并发访问的资源实现一个简单的读/写锁。工作人员随机尝试读取或写入共享对象。当设置读锁时,在释放锁之前,工作人员应该不能进行写操作。设置写锁时,不允许读写。 虽然我的实现看起来很有效,但我认为它在概念上是错误的 发生的读操作应允许同时发生更多的读操作,从而导致总的读操作数大于写操作数。我的程序生成的数字跟随着工人执行这些操作的概率 我觉得我的实现实际上根本不是并发的,但我很难识别错误。如果能给我指出正确的方向,我将不胜感激 分派和终止工人的主类:在Java中实现资源读/写锁,java,concurrency,locking,synchronized,Java,Concurrency,Locking,Synchronized,我试图为多个线程并发访问的资源实现一个简单的读/写锁。工作人员随机尝试读取或写入共享对象。当设置读锁时,在释放锁之前,工作人员应该不能进行写操作。设置写锁时,不允许读写。 虽然我的实现看起来很有效,但我认为它在概念上是错误的 发生的读操作应允许同时发生更多的读操作,从而导致总的读操作数大于写操作数。我的程序生成的数字跟随着工人执行这些操作的概率 我觉得我的实现实际上根本不是并发的,但我很难识别错误。如果能给我指出正确的方向,我将不胜感激 分派和终止工人的主类: class Main {
class Main {
private static final int THREAD_NUMBER = 4;
public static void main(String[] args) {
// creating workers
Thread[] workers = new Thread[THREAD_NUMBER];
for (int i = 0; i < THREAD_NUMBER; i++) {
workers[i] = new Thread(new Worker(i + 1));
}
System.out.println("Spawned workers: " + THREAD_NUMBER);
// starting workers
for (Thread t : workers) {
t.start();
}
try {
Thread.sleep((long) 10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// stopping workers
System.out.println("Stopping workers...");
for (Thread t : workers) {
t.interrupt();
}
}
}
最后是工人阶级:
import java.util.Random;
class Worker implements Runnable {
private static final double WRITE_PROB = 0.5;
private static Random rand = new Random();
private Resource res;
private int id;
public Worker(int id) {
res = Resource.getInstance();
this.id = id;
}
public void run() {
message("Started.");
while (!Thread.currentThread().isInterrupted()) {
performAction();
}
}
private void message(String msg) {
System.out.println("Worker " + id + ": " + msg);
}
private void read() {
synchronized(res) {
while (res.getWriteLock() == Resource.ResourceLock.ON) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
res.setReadLock();
// perform read
try {
Thread.sleep((long) 500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
res.releaseReadLock();
res.notifyAll();
}
message("Finished reading.");
}
private void write() {
synchronized(res) {
while (res.getWriteLock() == Resource.ResourceLock.ON || res.getReadLock() == Resource.ResourceLock.ON) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
res.setWriteLock();
// perform write
try {
Thread.sleep((long) 500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
res.releaseWriteLock();
res.notifyAll();
}
message("Finished writing.");
}
private void performAction() {
double r = rand.nextDouble();
if (r <= WRITE_PROB) {
write();
} else {
read();
}
}
}
非常感谢您的帮助。您正在一个
同步的
块中执行整个操作,因此没有并发性。此外,对于任何锁类型都没有优先级,因为最多一个线程可以拥有一个锁。如果不在synchronized
块中执行整个操作,则无法处理当前代码,因为无论有多少读卡器,每个读卡器都会在最后执行readLock=ResourceLock.OFF
。如果没有计数器,则无法正确支持多个读卡器
除此之外,它是一种奇怪的代码结构,提供了一个维护状态的资源类,但完全由调用方来处理它。这不是处理责任和封装的方法
实现可能看起来像
class ReadWriteLock {
static final int WRITE_LOCKED = -1, FREE = 0;
private int numberOfReaders = FREE;
private Thread currentWriteLockOwner;
public synchronized void acquireReadLock() throws InterruptedException {
while(numberOfReaders == WRITE_LOCKED) wait();
numberOfReaders++;
}
public synchronized void releaseReadLock() {
if(numberOfReaders <= 0) throw new IllegalMonitorStateException();
numberOfReaders--;
if(numberOfReaders == FREE) notifyAll();
}
public synchronized void acquireWriteLock() throws InterruptedException {
while(numberOfReaders != FREE) wait();
numberOfReaders = WRITE_LOCKED;
currentWriteLockOwner = Thread.currentThread();
}
public synchronized void releaseWriteLock() {
if(numberOfReaders!=WRITE_LOCKED || currentWriteLockOwner!=Thread.currentThread())
throw new IllegalMonitorStateException();
numberOfReaders = FREE;
currentWriteLockOwner = null;
notifyAll();
}
}
注意,我在这里避免使用全局变量。锁应该传递给构造函数。同样重要的是,在锁获取过程中被中断时,方法返回。像在原始代码中一样,自中断和重试采集将导致无限循环,因为在恢复当前线程的中断状态后,下一次等待将再次抛出InterruptedException
。当然,在没有锁的情况下继续操作也是错误的,因此唯一有效的选择是不恢复中断状态或立即返回
对主程序的唯一更改是构造一个pass-The-lock实例:
ReadWriteLock sharedLock = new ReadWriteLock();
// creating workers
Thread[] workers = new Thread[THREAD_NUMBER];
for (int i = 0; i < THREAD_NUMBER; i++) {
workers[i] = new Thread(new Worker(i + 1, sharedLock));
}
System.out.println("Spawned workers: " + THREAD_NUMBER);
// starting workers
for (Thread t : workers) {
t.start();
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// stopping workers
System.out.println("Stopping workers...");
for (Thread t : workers) {
t.interrupt();
}
ReadWriteLock sharedLock=new ReadWriteLock();
//创造工人
线程[]工作线程=新线程[线程编号];
对于(int i=0;i
这是ReadWriteLock
的简单实现,写入操作的优先级更高:
public class ReadWriteLock{
private int readers = 0;
private int writers = 0;
private int writeRequests = 0;
public synchronized void lockRead() throws InterruptedException{
while(writers > 0 || writeRequests > 0){
wait();
}
readers++;
}
public synchronized void unlockRead(){
readers--;
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
while(readers > 0 || writers > 0){
wait();
}
writeRequests--;
writers++;
}
public synchronized void unlockWrite() throws InterruptedException{
writers--;
notifyAll();
}
}
来源:为什么您认为您的实现不是并发的?正如您所看到的,发生的操作日志的读写分布大致相同。不应该是这种情况,因为读操作具有较少的限制性锁,从长远来看应该会导致更多的读操作。因此,我想问题在于,在我的理解中,如果这些工作人员同时工作,似乎不会出现编写器饥饿。如果您使用同步
进行访问,那么您就拥有一个独占锁。您必须从头开始构建自己的锁定机制。只要整个操作都在synchronized
块中,就不会有任何并发性。一旦您将操作移出synchronized
块,它将像每个读卡器一样在最后断开readLock=ResourceLock.OFF
,无论有多少读卡器。这在概念层面上已经行不通了。您需要一个计数器来记住读卡器的数量,以支持多个读卡器。除此之外,您应该将逻辑放入Resource
类中,而不是使其成为白盒结构,并希望调用方正确实现逻辑。
class Worker implements Runnable {
private static final double WRITE_PROB = 0.5;
private static final Random rand = new Random();
private final ReadWriteLock theLock;
private final int id;
public Worker(int id, ReadWriteLock lock) {
theLock = lock;
this.id = id;
}
public void run() {
message("Started.");
while(!Thread.currentThread().isInterrupted()) {
performAction();
}
}
private void message(String msg) {
System.out.println("Worker " + id + ": " + msg);
}
private void read() {
try {
theLock.acquireReadLock();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
// perform read
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
finally { theLock.releaseReadLock(); }
message("Finished reading.");
}
private void write() {
try {
theLock.acquireWriteLock();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
// perform write
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
finally { theLock.releaseWriteLock(); }
message("Finished writing.");
}
private void performAction() {
double r = rand.nextDouble();
if (r <= WRITE_PROB) {
write();
} else {
read();
}
}
}
ReadWriteLock sharedLock = new ReadWriteLock();
// creating workers
Thread[] workers = new Thread[THREAD_NUMBER];
for (int i = 0; i < THREAD_NUMBER; i++) {
workers[i] = new Thread(new Worker(i + 1, sharedLock));
}
System.out.println("Spawned workers: " + THREAD_NUMBER);
// starting workers
for (Thread t : workers) {
t.start();
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// stopping workers
System.out.println("Stopping workers...");
for (Thread t : workers) {
t.interrupt();
}
public class ReadWriteLock{
private int readers = 0;
private int writers = 0;
private int writeRequests = 0;
public synchronized void lockRead() throws InterruptedException{
while(writers > 0 || writeRequests > 0){
wait();
}
readers++;
}
public synchronized void unlockRead(){
readers--;
notifyAll();
}
public synchronized void lockWrite() throws InterruptedException{
writeRequests++;
while(readers > 0 || writers > 0){
wait();
}
writeRequests--;
writers++;
}
public synchronized void unlockWrite() throws InterruptedException{
writers--;
notifyAll();
}
}