读/写锁同步数据存取

Java 9并发编程指南 目录

读/写锁同步数据存取

之一。此类包含两种锁:一种是读取操作锁,一种是写入操作锁。多个线程能够同时使用读操作锁,但是只有一个线程可以使用写操作锁。如果一个线程正在写操作,其它线程均不可以读写。

在本节中,学习如何使用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接口的读锁时,不能修改变量值。否则,可能出现与不一致性相关的数据错误。

更多关注

  • 本章中”锁同步代码块”小节。
  • 第九章“测试并发应用”中的“监控锁接口”小节。
在下面这段代码中,哪些内容实现了同步:// 函数 static ssize_t finaldemo_read(struct file *filp,char *buf,size_t len,loff_t *off) { if(wait_event_interruptible(finaldemo.outq,finaldemo.flag!=0)) //不可时 阻塞进程 { return -ERESTARTSYS; } if(down_interruptible(&finaldemo.sem)) //P 操作 { return -ERESTARTSYS; } finaldemo.flag = 0; printk("into the read function\n"); printk("the rd is %c\n",finaldemo.rd); //指针 if(finaldemo.rd < finaldemo.wr) len = min(len,(size_t)(finaldemo.wr - finaldemo.rd)); //更新长度 else len = min(len,(size_t)(finaldemo.end - finaldemo.rd)); printk("the len is %d\n",len); if(raw_copy_to_user(buf,finaldemo.rd,len)) { printk(KERN_ALERT"copy failed\n"); / up递增信号量的值,并唤醒所有正在等待信号量转为可用状态的进程。 必须小心使用信号量。被信号量保护的数据必须是定义清晰的,并且存取这些数据的所有代码都必须首先获得信号量。 */ up(&finaldemo.sem); return -EFAULT; } printk("the read buffer is %s\n",finaldemo.buffer); finaldemo.rd = finaldemo.rd + len; if(finaldemo.rd == finaldemo.end) finaldemo.rd = finaldemo.buffer; //字符缓冲区循环 up(&finaldemo.sem); //V 操作 return len; } static ssize_t finaldemo_write(struct file *filp,const char *buf,size_t len,loff_t *off) { if(down_interruptible(&finaldemo.sem)) //P 操作 { return -ERESTARTSYS; } while(spacefree(&finaldemo) == 0) //检查剩余空间 { up(&finaldemo.sem); //释放信号量 if(filp->f_flags & O_NONBLOCK) return -EAGAIN; if(wait_event_interruptible(finaldemo.inq,(spacefree(&finaldemo) > 0))) return -ERESTARTSYS; if(down_interruptible(&finaldemo.sem)) return -ERESTARTSYS; } if(finaldemo.rd <= finaldemo.wr) len = min(len,(size_t)(finaldemo.end - finaldemo.wr)); else len = min(len,(size_t)(finaldemo.rd-finaldemo.wr-1)); printk("the write len is %d\n",len); if(raw_copy_from_user(finaldemo.wr,buf,len)) { up(&finaldemo.sem); //V 操作 return -EFAULT; } printk("the write buffer is %s\n",finaldemo.buffer); printk("the len of buffer is %d\n",strlen(finaldemo.buffer)); finaldemo.wr = finaldemo.wr + len; if(finaldemo.wr == finaldemo.end) finaldemo.wr = finaldemo.buffer; //循环 up(&finaldemo.sem); //V 操作 finaldemo.flag=1; //条件成立,可以唤醒进程 wake_up_interruptible(&finaldemo.outq); //唤醒进程 return len; } module_init(finaldemo_init); module_exit(finaldemo_exit); MODULE_LICENSE("GPL");
06-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值