[Java并发编程]之二十:并发新特性―Lock锁和条件变量(含代码)(四)

2014-11-24 07:37:05 · 作者: · 浏览: 1
terruptedException e) { System.out.println(我不读了); } System.out.println(读结束); } } class Writer2 extends Thread { private BufferInterruptibly buff; public Writer2(BufferInterruptibly buff) { this.buff = buff; } @Override public void run() { buff.write(); } }

执行结果如下:

\ 从结果中可以看出,尝试中断后输出了catch语句块中的内容,也输出了后面的“读结束”,说明线程对互斥锁的等待被中断了,也就是该互斥锁响应了对读线程的中断。

条件变量实现线程间协作


生产者——消费者模型一文中,我们用synchronized实现互斥,并配合使用Object对象的wait()和notify()或notifyAll()方法来实现线程间协作。Java 5之后,我们可以用Reentrantlock锁配合Condition对象上的await()和signal()或signalAll()方法来实现线程间协作。在ReentrantLock对象上newCondition()可以得到一个Condition对象,可以通过在Condition上调用await()方法来挂起一个任务(线程),通过在Condition上调用signal()来通知任务,从而唤醒一个任务,或者调用signalAll()来唤醒所有在这个Condition上被其自身挂起的任务。另外,如果使用了公平锁,signalAll()的与Condition关联的所有任务将以FIFO队列的形式获取锁,如果没有使用公平锁,则获取锁的任务是随机的,这样我们便可以更好地控制处在await状态的任务获取锁的顺序。与notifyAll()相比,signalAll()是更安全的方式。另外,它可以指定唤醒与自身Condition对象绑定在一起的任务。
下面将 生产者——消费者模型一文中的代码改为用条件变量实现,如下:
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

class Info{	// 定义信息类
	private String name = name;//定义name属性,为了与下面set的name属性区别开
	private String content = content ;// 定义content属性,为了与下面set的content属性区别开
	private boolean flag = true ;	// 设置标志位,初始时先生产
	private Lock lock = new ReentrantLock();  
	private Condition condition = lock.newCondition(); //产生一个Condition对象
	public  void set(String name,String content){
		lock.lock();
		try{
			while(!flag){
				condition.await() ;
			}
			this.setName(name) ;	// 设置名称
			Thread.sleep(300) ;
			this.setContent(content) ;	// 设置内容
			flag  = false ;	// 改变标志位,表示可以取走
			condition.signal();
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}finally{
			lock.unlock();
		}
	}

	public void get(){
		lock.lock();
		try{
			while(flag){
				condition.await() ;
			}	
			Thread.sleep(300) ;
			System.out.println(this.getName() + 
				 -->  + this.getContent()) ;
			flag  = true ;	// 改变标志位,表示可以生产
			condition.signal();
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}finally{
			lock.unlock();
		}
	}

	public void setName(String name){
		this.name = name ;
	}
	public void setContent(String content){
		this.content = content ;
	}
	public String getName(){
		return this.name ;
	}
	public String getContent(){
		return this.content ;
	}
}
class Producer implements Runnable{	// 通过Runnable实现多线程
	private Info info = null ;		// 保存Info引用
	public Producer(Info info){
		this.info = info ;
	}
	public void run(){
		boolean flag = true ;	// 定义标记位
		for(int i=0;i<10;i++){
			if(flag){
				this.info.set(姓名--1,内容--1) ;	// 设置名称
				flag = false ;
			}else{
				this.info.set(姓名--2,内容--2) ;	// 设置名称
				flag = true ;
			}
		}
	}
}
class Consumer implements Runnable{
	private Info info = null ;
	public Consumer(Info info){
		this.info = info ;
	}
	public void run(){
		for(int i=0;i<10;i++){
			this.info.get() ;
		}
	}
}
public class ThreadCaseDemo{
	public static void main(String args[]){
		Info info = new Info();	// 实例化Info对象
		Producer pro = new Producer(info) ;	// 生产者
		Consumer con = new Consumer(info) ;	// 消费者
		new Thread(pro).start() ;
		//启动了生产者线程后,再启动消费者线程
		try{
			Thread.sleep(500) ;
		}catch(InterruptedException e){
			e.printStackTrace() ;
		}

		new Thread(con).start() ;
	}
}
执行后,同样可以得到如下的结果: 姓名--1 --> 内容--1
姓名--2 --> 内容--2
姓名--1 --> 内容--1
姓名--2 --> 内容--2
姓名--1 --> 内容--1
姓名--2 --> 内容--2
姓名--1 --> 内容--1
姓名--2 --> 内容--2
姓名--1 --> 内容--1
姓名--2 --> 内容--2
从以上并不能看出用条件变量的await()、signal()、signalAll()方法比用Object对象的wait()、notify()、notifyAll()方法实现线程间协作有多少优点,但它在处理更复杂的多线程问题时,会有明显的优势。所以,Lock和Condition对象只有在更加困难的多线程问题中才是必须的。

读写锁

另外,synchronized获取的互斥锁不仅互斥读写操作、写写操作,还互斥读读操作,而读读操作时不会带来数据竞争的,因此对对读读操作也互斥的话,会降低性能。Java 5中提供了读写锁,它将读锁和写锁分离,使得读读操作不互斥,获取读锁和写锁的一般形式如下:

ReadWriteLock rwl = new ReentrantReadWriteLock();	 
rwl.writeLock().lock()  //获取写锁
rwl.readLock().lock()  //获取读锁
用读锁来锁定读操作,用写锁来锁定写操作,这样写操