用读写锁来同步数据访问

锁提供的最显著的改进之一是 ReadWriteLock 接口和 ReentrantReadWriteLock 类。 此类有2个锁,一个供读操作,一个供写操作:

  • 可以同时有多个线程进行读操作,但是只允许一个线程进行写操作
  • 当一个线程在执行写操作时,不能有线程进行读操作

任务

用 ReadWriteLock 接口来控制对存储有2个产品的价格的访问。

实现

  • 数据类 (PricesInfo)

    • 2 个double类型属性 price1, price2
        private double price1;
        private double price2;
    
    • ReadWriteLock 对象名为 lock
        private ReadWriteLock lock;
    
    • 在构造函数中初始化上述3个属性; 对于 lock , 创建一个 ReentrantReadWriteLock 对象
        public PricesInfo() {
            price1 = 1.0;
            price2 = 2.0;
            lock = new ReentrantReadWriteLock();
        }
    
    • getPrice1() 方法: 使用了读锁来控制对此属性的访问
        public double getPrice1() {
            lock.readLock().lock();
            double value = price1;
            lock.readLock().unlock();
            return value;
        }
    
    • 类似 getPrice1() 编写 getPrice2() 方法
    • setPrices 方法设置 2 个价格属性的值: 使用了写锁来控制对它们的访问
        public void setPrices(double price1, double price2) {
            lock.writeLock().lock();
            this.price1 = price1;
            this.price2 = price2;
            lock.writeLock().unlock();
        }
    
  • 线程类 : Reader

    • 声明一个 PricesInfo 对象并在构造函数中初始化此对象
        private PricesInfo pricesInfo;
    
        public Reader(PricesInfo pricesInfo) {
            this.pricesInfo = pricesInfo;
        }
    
    • run方法 : 读多次2个价格的值
            for (int i = 0; i < 10; i++) {
                System.out.printf("%s: Price 1: %f, Price 2: %f\n",
                        Thread.currentThread().getName(), pricesInfo.getPrice1(), pricesInfo.getPrice2());
            }
    
  • 线程类 : Writer

    • 声明一个 PricesInfo 对象并在构造函数中初始化此对象
        private PricesInfo pricesInfo;
    
        public Writer(PricesInfo pricesInfo) {
            this.pricesInfo = pricesInfo;
        }
    
    • run方法 : 修改多次2个价格的值,每次睡上2毫秒
            for (int i = 0; i < 3; i++) {
                System.out.printf("Writer: Attempt to modify the prices.\n");
                pricesInfo.setPrices(Math.random() * 10, Math.random() * 8);
                System.out.printf("Writer: Prices have been modified.\n");
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
  • 控制类 : Main

    • 创建 PricesInfo 对象
    • 创建多个 Reader 对象及对应的线程以执行它们
    • 创建一个 Writer 对象及对应的线程
    • 启动这些线程

工作原理

ReentrantReadWriteLock 有2个锁,一个给读操作,一个给写操作。

  • 用在读操作中的锁通过在 ReadWriteLock 接口中声明的 readLock() 方法获得
    • 此锁是一个实现了 Lock 接口的对象,可用 lock(), unlock() 和 tryLock() 方法
  • 用在写操作中的锁通过在 ReadWriteLock 接口中声明的 writeLock() 方法获得
    • 此锁也是一个实现了 Lock 接口的对象,可用 lock(), unlock() 和 tryLock() 方法

确保这些锁的正确使用、按它们的设计目的使用是程序员的责任。拿到读锁时,不能修改变量的值。否则会有数据不一致的错误。