Jan*_*dło 3 java concurrency locking synchronized
我正在尝试为多个线程并发访问的资源实现一个简单的读/写锁。工作人员随机尝试读取或写入共享对象。当设置了读锁时,工作人员在锁被释放之前应该不能写。当设置了写锁时,不允许读和写。虽然我的实现似乎有效,但我认为它在概念上是错误的。
发生的读操作应该允许同时发生更多的读操作,从而导致总读取次数大于写入次数。我的程序产生的数字遵循工人执行这些操作的概率。
我觉得我的实现实际上根本不是并发的,但是我很难识别错误。我真的很感激被指出正确的方向。
调度和终止工人的主类:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
资源类:
class Resource {
enum ResourceLock {
ON,
OFF
}
private static Resource instance = null;
private ResourceLock writeLock = ResourceLock.OFF;
private ResourceLock readLock = ResourceLock.OFF;
private Resource() {}
public static synchronized Resource getInstance() {
if (instance == null) {
instance = new Resource();
}
return instance;
}
public ResourceLock getWriteLock() {
return writeLock;
}
public ResourceLock getReadLock() {
return readLock;
}
public void setWriteLock() {
writeLock = ResourceLock.ON;
}
public void setReadLock() {
readLock = ResourceLock.ON;
}
public void releaseWriteLock() {
writeLock = ResourceLock.OFF;
}
public void releaseReadLock() {
readLock = ResourceLock.OFF;
}
}
Run Code Online (Sandbox Code Playgroud)
最后是 Worker 类:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
有两个单独的读锁和写锁背后的原因是我希望能够将操作和它们对锁的查询原子化。
这是我以 0.5 写入概率获得的输出示例:
Spawned workers: 4
Worker 2: Started.
Worker 3: Started.
Worker 1: Started.
Worker 4: Started.
Worker 2: Finished writing.
Worker 4: Finished reading.
Worker 1: Finished writing.
Worker 3: Finished writing.
Worker 1: Finished reading.
Worker 4: Finished writing.
Worker 2: Finished reading.
Worker 4: Finished reading.
Worker 1: Finished reading.
Worker 3: Finished writing.
Worker 1: Finished writing.
Worker 4: Finished writing.
Worker 2: Finished writing.
Worker 4: Finished writing.
Worker 1: Finished reading.
Worker 3: Finished writing.
Worker 1: Finished writing.
Worker 4: Finished reading.
Worker 2: Finished writing.
Stopping workers...
Worker 4: Finished writing.
Worker 1: Finished writing.
Worker 3: Finished reading.
Worker 2: Finished reading.
Run Code Online (Sandbox Code Playgroud)
非常感谢帮助。
您正在一个synchronized块内执行整个操作,因此没有并发。此外,任何锁类型都没有优先级,因为最多一个线程可以拥有一个锁。不在synchronized块中执行整个操作不适用于您当前的代码,因为每个读者都会readLock = ResourceLock.OFF最后,无论那里有多少读取器。没有计数器,您将无法正确支持多个阅读器。
除此之外,这是一个奇怪的代码结构,提供一个 Resource类来维护状态,但完全由调用者来做正确的事情。这不是处理责任和封装的方式。
一个实现可能看起来像
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();
}
}
Run Code Online (Sandbox Code Playgroud)
它只是使用获取读锁的计数器,将计数器设置-1为有写锁时(因此写锁不能嵌套)。只要没有写锁,获取读锁就可能成功,所以不需要为它们实现优先级,当另一个线程已经有真正的锁时,成功的可能性就足够了。事实上,当读者数量明显多于作家时,您可能会遇到“作家饥饿”问题。
工人简化为
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我在这里避免了全局变量。锁应该传递给构造函数。方法在获取锁期间被中断时返回也很重要。像在原始代码中那样自我中断并重试获取将导致无限循环,因为下一次等待将再次抛出InterruptedException在您恢复当前线程的中断状态后。当然,在没有锁的情况下继续操作也是错误的,因此唯一有效的选择是不恢复中断状态或立即返回。
对主程序的唯一更改是构造一个 pass 锁实例:
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();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4211 次 |
| 最近记录: |