Java并发编程实战手册第二章关于锁的问题。
2.6使用读写锁实现同步数据访问
通过本范例学习ReadWriteLock和实现类,ReentrantReadWriteLock,该类有两个锁一个是读操作锁,一个是写操作锁。读操作锁可以允许多个线程同时访问,写操作锁只允许一个线程进行。当写操作执行的时候,读操作不能执行
1. 创建PriceInfo,Reader,Writer,Main.
1. PriceInfo.java
1. PriceInfo.java
package cn.fans.chapter2.six;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
*
* @author fcs
* @date 2015-4-12
* 描述:使用读写锁实现同步访问
* 说明:使用ReadWriteLock接口。
*/
public class PricesInfo {
private double price1;
private double price2;
private ReadWriteLock lock;
public PricesInfo(){
price1 = 1.0;
price2 = 2.0;
lock = new ReentrantReadWriteLock();
}
/**
*
* 作者:fcs
* 描述:使用锁机制进行读操作
* 说明:
* 返回:price1
* 参数:
* 时间:2015-4-12
*/
public double getPrice1(){
double value;
lock.readLock().lock();
try{
value = price1;
System.out.printf("%s: price 1 : %f\n",Thread.currentThread().getName(),price1);
}finally{
lock.readLock().unlock();
}
return value;
}
/**
*
* 作者:fcs
* 描述:使用锁机制进行读操作
* 说明:
* 返回:price2
* 参数:
* 时间:2015-4-12
*/
public double getPrice2(){
double value;
lock.readLock().lock();
try{
value = price2;
System.out.printf("%s Price 2 : %f\n ",Thread.currentThread().getName(),price2);
}finally{
lock.readLock().unlock();
}
return value;
}
/**
*
* 作者:fcs
* 描述:使用锁机制进行写操作
* 说明:
* 返回:
* 参数:price1, price2
* 时间:2015-4-12
*/
public void setPrice(double price1,double price2){
lock.writeLock().lock();
System.out.printf("Writer: Attempt to modify the prices.\n");
try{
this.price1 = price1;
this.price2 = price2;
}finally{
lock.writeLock().unlock();
System.out.println("Writer:price have been modified.");
}
}
}
package cn.fans.chapter2.six;
/**
*
* @author fcs
* @date 2015-4-12
* 描述:读线程
* 说明:
*/
public class Reader implements Runnable {
private PricesInfo pricesInfo;
public Reader(PricesInfo pricesInfo) {
this.pricesInfo = pricesInfo;
}
@Override
public void run() {
for(int i =0;i<10;i++){
pricesInfo.getPrice1();
pricesInfo.getPrice2();
}
}
}
package cn.fans.chapter2.six;
/**
*
* @author fcs
* @date 2015-4-12
* 描述:写线程
* 说明:
*/
public class Writer implements Runnable {
private PricesInfo pricesInfo;
public Writer(PricesInfo pricesInfo) {
this.pricesInfo = pricesInfo;
}
/**
* 循环修改两个价格5次,每次修改后线程将休眠2秒钟
*/
@Override
public void run() {
for(int i=0;i< 5;i++){
pricesInfo.setPrice(Math.random()*10, Math.random()*10);
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package cn.fans.chapter2.six;
public class Main {
public static void main(String[] args) {
PricesInfo pricesInfo = new PricesInfo();
Reader readers[] = new Reader[5];
Thread treader [] = new Thread[5];
for(int i =0;i<5;i++){
readers [i] = new Reader(pricesInfo);
treader[i] = new Thread(readers[i]);
}
Writer writer = new Writer(pricesInfo);
Thread wthread = new Thread(writer);
for(int i = 0;i < 5;i++){
treader[i].start();
}
wthread.start();
}
}
创建5个读线程,一个写线程,读线程不互斥,写线程写的时候,读线程读到的信息必须是写线程写完后的新数据。这里如果按照书上的代码,可能会出现非常令人迷惑的输出,关键在与Writer线程中的run方法多了两条输出语句,这两条应该在priceInfo中的setPrice方法中的,我在并发编程网上看到有人说要放在这里,同样csdn上也获得了相同的指导,在实际的运行过程中才跟书上的一致。
深入分析就是Writer线程是使用for循环进行写入的,也就是说在for循环中的两条输出语句中间执行priceinfo.setPrice方法,是有延迟的,导致每次修改前后两条输出语句并不是连续输出的,这是因为在输出第一条语句的时候,写线程正在获取锁,其他读线程可能正在读写线程上一次修改的值,当输出第二条语句的时候,有的读线程读到新的值,而有的读到旧的值。这就出现了一种幻觉,认为读线程读到了脏数据。所以应该将这两条放到setPrice中的lock(),和unlock()方法下。这样才会输出两条连续的提示语句,然后读线程会在输出语句后读出一致的值。