Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/400.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中实现资源读/写锁_Java_Concurrency_Locking_Synchronized - Fatal编程技术网

在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();
  }
}