读/写锁同步数据存取

读/写锁同步数据存取


ReadWriteLock接口和实现此接口的ReentrantReadWriteLock特殊类是锁机制提供的最显著改进之一。此类包含两种锁:一种是读取操作锁,一种是写入操作锁。多个线程能够同时使用读操作锁,但是只有一个线程可以使用写操作锁。如果一个线程正在写操作,其它线程均不可以读写。

在本节中,学习如何使用ReadWriteLock接口,实现程序用此接口控制存储两种产品价格的对象。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

###实现过程

通过如下步骤完成范例:

  1. 创建名为PricesInfo的类,用来存储两种产品的价格信息:

    public class PricesInfo {
    
  2. 定义两个名为price1和price2的double类型属性:

    	private double price1;
    	private double price2;
    
  3. 定义名为lock的ReadWriteLock对象:

    	private ReadWriteLock lock;
    
  4. 实现类构造函数,初始化三个属性。其中为lock属性初始化新的ReentrantReadWriteLock对象:

    	public PricesInfo(){
    		price1 = 1.0;
    		price2 = 2.0;
    		lock = new ReentrantReadWriteLock();
    	}
    
  5. 实现getPrice1()方法,返回price1属性值,使用读锁来控制使用此属性值:

    	public double getPrice1() {
    		lock.readLock().lock();
    		double value = price1;
    		lock.readLock().unlock();
    		return value;
    	}
    
  6. 实现getPrice2()方法,返回price2属性值,使用读锁来控制使用此属性值:

    	public double getPrice2() {
    		lock.readLock().lock();
    		double value = price2;
    		lock.readLock().unlock();
    		return value;
    	}
    
  7. 实现setPrices()方法来确定两个使用写锁来控制使用的属性值。设置线程休眠10秒钟,这表明尽管它在使用写锁,但也没有其它线程控制读锁:

    	public void setPrices(double price1, double price2){
    		lock.writeLock().lock();
    		System.out.printf("%s: PricesInfo: Write Lock Adquired.\n", new Date());
    		try {
    			TimeUnit.SECONDS.sleep(10);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		this.price1 = price1;
    		this.price2 = price2;
    		System.out.printf("%s: PricesInfo: Write Lock Released.\n", new Date());
    		lock.writeLock().unlock();
    	}
    
  8. 创建名为Reader的类,指定其实现Runnable接口。此类实现一个读取程序,获得PricesInfo类属性的值:

    public class Reader implements Runnable {
    
  9. 定义PricesInfo对象,实现初始化此对象的类构造函数:

    	private PricesInfo pricesInfo;
    	
    	public Reader (PricesInfo pricesInfo) {
    		this.pricesInfo = pricesInfo;
    	}
    
  10. 实现此类的run()方法,读取10遍两种商品的价格:

    	@Override
    	public void run() {
    		for( int i = 0 ; i < 10 ; i++){
    			System.out.printf("%s : %s : Price 1 : %f\n", new Date(), Thread.currentThread().getName(), pricesInfo.getPrice1());
    			System.out.printf("%s : %s : Price 2 : %f\n", new Date(), Thread.currentThread().getName(), pricesInfo.getPrice2());
    		}
    	}
    
  11. 创建名为Reader的类,指定其实现Runnable接口。此类实现一个修改程序,设置PricesInfo类属性的值:

    public class Writer implements Runnable{
    
  12. 定义PricesInfo对象,实现初始化此对象的类构造函数:

    	private PricesInfo pricesInfo;
    	
    	public Writer (PricesInfo pricesInfo) {
    		this.pricesInfo = pricesInfo;
    	}
    
  13. 实现run()方法,循环修改两种商品价格三次,每次之间休眠2秒:

    	@Override
    	public void run() {
    		for(int i = 0 ; i < 3 ; i++){
    			System.out.printf("%s : Writer : Attempt to modify the prices.\n", new Date());
    			pricesInfo.setPrices(Math.random() * 10,  Math.random() * 8);
    			System.out.printf("%s : Writer : Prices have been modified.\n", new Date());
    			try {
    				Thread.sleep(2);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
  14. 创建本范例中的主类,实现一个包含main()方法的Main类:

    public class Main {
    	public static void main(String[] args) {
    
  15. 创建PricesInfo对象:

    		PricesInfo pricesInfo = new PricesInfo();
    
  16. 创建五个Reader对象和五个执行它们的Thread对象:

    		Reader readers[] = new Reader[5];
    		Thread threadsReader[] = new Thread[5];
    		for (int i = 0 ; i < 5 ; i ++){
    			readers[i] = new Reader(pricesInfo);
    			threadsReader[i] = new Thread(readers[i]);
    		}
    
  17. 创建Writer对象和执行它的Thread对象:

    		Writer writer = new Writer(pricesInfo);
    		Thread threadWriter = new Thread(writer);
    
  18. 启动读写线程:

    		for (int i = 0 ; i < 5 ; i ++){
    			threadsReader[i].start();
    		}
    		threadWriter.start();
    

工作原理

下图显示执行本范例输出的部分内容:

pics/02_05.jpg

当写入程序控制写锁时,所有读取程序都无法得到数据。通过控制台可以看到Write Lock Acquired信息后面有一些读取程序信息,这些时之前已经执行完并且没有实时输出的指令信息。一旦写入程序释放了写锁,读取程序将再次获得访问价格信息的权限,显示新的价格。

如之前所述,ReentrantReadWriteLoc类包含两种锁:一种是读取操作锁,一种是写入操作锁。读取操作使用的锁通过ReadWriteLock接口定义的readLock()方法获得。读锁是实现Lock接口的对象,所以可以使用lock()、unlock()、和tryLock()方法。写入操作使用的锁通过ReadWriteLock接口定义的writeLock()方法获得。写锁也是实现Lock接口的对象,所以可以使用lock()、unlock()、和tryLock()方法。开发人员的职责是确保正确使用这些锁,与其初始设计的使用目的相同。当使用Lock接口的读锁时,不能修改变量值。否则,可能出现与不一致性相关的数据错误。

更多关注

  • 本章中”锁同步代码块”小节。
  • 第九章“测试并发应用”中的“监控锁接口”小节。

猜你喜欢

转载自blog.csdn.net/nicolastsuei/article/details/84503887