首页 > 解决方案 > Java:如何解决读写器问题?

问题描述

我想为读者 - 作家问题实施解决方案。主要规则是,一次只有一个写入者可以写入,其他写入者或读取者都不能写入或读取,但如果写入者不写入,则多个读取者可以读取。在主类中,我尝试运行线程,executorService.execute但我猜我遇到了一些问题。我不太了解executorService。该程序永远不会结束,我猜有一些输出问题。

我的代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;


public class ReaderWriter {
public static void main(String [] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    ReadWriteLock RW = new ReadWriteLock();


    executorService.execute(new Writer(RW));
    executorService.execute(new Writer(RW));
    executorService.execute(new Writer(RW));
    executorService.execute(new Writer(RW));

    executorService.execute(new Reader(RW));
    executorService.execute(new Reader(RW));
    executorService.execute(new Reader(RW));
    executorService.execute(new Reader(RW));
 }
}


class ReadWriteLock{
    static Semaphore readLock = new Semaphore(1);
    static Semaphore writeLock = new Semaphore(1);
    volatile static int readCount = 0;

    public void readLock() throws InterruptedException {

        readLock.acquire();
        readCount++;
        if (readCount == 1) {
            writeLock.acquire();
        }
        readLock.release();

        //Reading section
        System.out.println("Thread "+Thread.currentThread().getName() + " is READING");
        Thread.sleep(1500);
        System.out.println("Thread "+Thread.currentThread().getName() + " has FINISHED READING");

        //Releasing section
        readLock.acquire();
        readCount--;
        if(readCount == 0) {
            writeLock.release();
        }
        readLock.release();
    }
    public void writeLock() throws InterruptedException {
        writeLock.acquire();
        System.out.println("Thread "+Thread.currentThread().getName() + " is WRITING");
        Thread.sleep(2500);
        writeLock.release();
        System.out.println("Thread "+Thread.currentThread().getName() + " has finished WRITING");
    }
}




class Writer implements Runnable
{
    private ReadWriteLock RW_lock;


    public Writer(ReadWriteLock rw) {
        RW_lock = rw;
    }

    public void run() {
        while (true){
            try {
                RW_lock.writeLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



class Reader implements Runnable
{
    private ReadWriteLock RW_lock;


    public Reader(ReadWriteLock rw) {
        RW_lock = rw;
    }
    public void run() {
        while (true){
            try {
                RW_lock.readLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}

我认为这个问题的输出不正确:

Thread pool-1-thread-1 is WRITING
Thread pool-1-thread-2 is WRITING
Thread pool-1-thread-1 has finished WRITING
Thread pool-1-thread-2 has finished WRITING
Thread pool-1-thread-3 is WRITING
Thread pool-1-thread-3 has finished WRITING
Thread pool-1-thread-4 is WRITING
Thread pool-1-thread-4 has finished WRITING
Thread pool-1-thread-5 is READING
Thread pool-1-thread-8 is READING
Thread pool-1-thread-7 is READING
Thread pool-1-thread-6 is READING
Thread pool-1-thread-8 has FINISHED READING
Thread pool-1-thread-5 has FINISHED READING
Thread pool-1-thread-8 is READING
Thread pool-1-thread-5 is READING
Thread pool-1-thread-6 has FINISHED READING
Thread pool-1-thread-6 is READING
Thread pool-1-thread-7 has FINISHED READING
Thread pool-1-thread-7 is READING
Thread pool-1-thread-5 has FINISHED READING
Thread pool-1-thread-5 is READING
Thread pool-1-thread-8 has FINISHED READING

在此输出中,有 2 位作家同时写作。

输出编辑:

Thread pool-1-thread-1 is WRITING
Thread pool-1-thread-1 has finished WRITING
Thread pool-1-thread-1 is WRITING
Thread pool-1-thread-1 has finished WRITING
Thread pool-1-thread-4 is WRITING
Thread pool-1-thread-4 has finished WRITING
Thread pool-1-thread-3 is WRITING
Thread pool-1-thread-3 has finished WRITING
Thread pool-1-thread-2 is WRITING
Thread pool-1-thread-2 has finished WRITING
Thread pool-1-thread-8 is READING
Thread pool-1-thread-7 is READING
Thread pool-1-thread-5 is READING
Thread pool-1-thread-6 is READING
Thread pool-1-thread-8 has FINISHED READING
Thread pool-1-thread-7 has FINISHED READING
Thread pool-1-thread-5 has FINISHED READING
Thread pool-1-thread-6 has FINISHED READING

标签: javamultithreadingconcurrencyparallel-processingsynchronization

解决方案


该程序永远不会结束,我猜有一些输出问题。

在类中添加一个标志ReadWriteLock以指示Threads它们何时应该停止工作:

private final AtomicBoolean keep_working = new AtomicBoolean(true);

在类中添加一个方法ReadWriteLock来通知线程停止:

public void stopThreads(){
    keep_working.set(false);
}

并添加查询标志的方法:

public boolean keepWorking(){
    return keep_working.get();
}

相应地调整WriterReader run方法:

 public void run() {
        while (RW_lock.keepWorking()){
           ...
        }
    }

类上添加对方法ExecutorService.awaitTermination()ReadWriteLock.stopThreads和的调用ExecutorService.shutdown()

public static void main(String [] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    ReadWriteLock RW = new ReadWriteLock();

    executorService.execute(new Writer(RW));
    executorService.execute(new Writer(RW));
    executorService.execute(new Writer(RW));
    executorService.execute(new Writer(RW));

    executorService.execute(new Reader(RW));
    executorService.execute(new Reader(RW));
    executorService.execute(new Reader(RW));
    executorService.execute(new Reader(RW));
    try {
        executorService.awaitTermination(5, TimeUnit.SECONDS);
    } catch (InterruptedException e) { // ...} 
    RW.stopThreads();
    executorService.shutdown();
}
    

我认为这个问题的输出不正确:(...)在这个输出中,有 2 位作家同时写作。

那是因为在:

public void writeLock() throws InterruptedException {
    writeLock.acquire();
    System.out.println("Thread "+Thread.currentThread().getName() + " is WRITING");
    Thread.sleep(2500);
    writeLock.release();
    System.out.println("Thread "+Thread.currentThread().getName() + " has finished WRITING");
}

您在打印“已完成写入”之前释放锁,因此,等待该锁被释放的线程进入并在第一个线程有时间打印“已完成写入”之前打印“正在写入”。因此,您需要将代码更改为:

   public void writeLock() throws InterruptedException {
        writeLock.acquire();
        System.out.println("Thread "+Thread.currentThread().getName() + " is WRITING");
        Thread.sleep(2500);
        System.out.println("Thread "+Thread.currentThread().getName() + " has finished WRITING");
        writeLock.release();
    }

主要规则是,一次只有一个写入者可以写入,其他写入者或读取者都不能写入或读取,但如果写入者不写入,则多个读取者可以读取。

实际上,您可以利用 Java ReadWriteLock接口。

ReadWriteLock 维护一对关联的锁,一个用于只读操作,一个用于写入。只要没有写者,读锁可能被多个读线程同时持有。写锁是独占的。所有 ReadWriteLock 实现必须保证 writeLock 操作的内存同步效果(如 Lock 接口中指定的那样)相对于关联的 readLock 也成立。也就是说,成功获取读锁的线程将看到在先前释放写锁时所做的所有更新。

与互斥锁相比,读写锁在访问共享数据时允许更高级别的并发性。它利用了这样一个事实:虽然一次只有一个线程(写入线程)可以修改共享数据,但在许多情况下,任意数量的线程可以同时读取数据(因此读取线程)。理论上,使用读写锁所允许的并发性的增加将导致比使用互斥锁的性能提高。在实践中,这种并发性的增加只有在多处理器上才能完全实现,而且只有在共享数据的访问模式合适的情况下。

通过使用该接口,您可以显着简化readLockandwriteLock方法,如下所示:

   public void readLock() throws InterruptedException {
        shared_resource.readLock().lock();
        System.out.println("Thread "+Thread.currentThread().getName() + " is READING");
        Thread.sleep(1500);
        System.out.println("Thread "+Thread.currentThread().getName() + " has FINISHED READING");
        shared_resource.readLock().unlock();
    }
    public void writeLock() throws InterruptedException {
        shared_resource.writeLock().lock();
        System.out.println("Thread "+Thread.currentThread().getName() + " is WRITING");
        Thread.sleep(2500);
        System.out.println("Thread "+Thread.currentThread().getName() + " has finished WRITING");
        shared_resource.writeLock().unlock();
    }

要完成,您应该添加一个计算写入和读取次数的变量。因此,如果没有写入任何内容,则读取线程应该等待,同时,写入线程应该写入一些东西,依此类推。


推荐阅读