话不多数先上代码
package cn.fans.chapter2.eight;
/**
*
* @author fcs
* @date 2015-4-15
* 描述:在锁中使用多个条件(multiple Condition)
* 说明:
*/
public class FileMock {
Object bul;
private String content [] ; //存储文件的内容
private int index; //从文件中读取内容的行号
/**
* 使用构造方法生成需要的数据
* @param size
* @param length
*/
public FileMock(int size,int length){
content = new String[size];
for(int i =0;i < size;i++){
StringBuffer buffer = new StringBuffer(length);
for(int j =0 ;j< length;j++){
int numb = (int)Math.random() * 255;
buffer.append((char)numb);
}
content[i] = buffer.toString();
}
index = 0;
}
/**
*
* 作者:fcs
* 描述:如果文件有可以处理的数据行则返回true.如果没有可以处理的数据则返回false
* 说明:
* 返回:
* 参数:
* 时间:2015-4-15
*/
public boolean hasMoreLines(){
return index < content.length;
}
/**
*
* 作者:fcs
* 描述:返回属性index指定的行内容,并将index自动增加1
* 说明:
* 返回:
* 参数:
* 时间:2015-4-15
*/
public String getLine(){
if(this.hasMoreLines()){
System.out.println("mock: "+(content.length - index));
return content[index++];
}
return null;
}
}
package cn.fans.chapter2.eight;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author fcs
* @date 2015-4-16
* 描述:缓冲区
* 说明:
*/
public class Buffer {
private LinkedList<String> buffer ; // 存放共享数据
private int maxsize; //存放buffer的长度
private ReentrantLock lock; //用来对修改buffer的代码进行控制。
private Condition lines; //控制行数的条件
private Condition space; //控制是否有数据的条件
boolean pendingLines ; //表示缓冲区是否有数据的条件
public Buffer(int maxsize){
this.maxsize = maxsize;
buffer = new LinkedList<String>();
lock = new ReentrantLock();
lines = lock.newCondition();
space = lock.newCondition();
pendingLines = true;
}
/**
*
* 作者:fcs
* 描述:将数据插入缓冲区,当缓冲区满的时候,线程等待,当缓冲区有数据的时候唤醒线程
* 说明:
* 返回:
* 参数:
* 时间:2015-4-15
*/
public void insert(String line){
lock.lock(); //获取锁
try {
while(buffer.size() == maxsize){
space.await(); //缓冲区满的时候等待
}
buffer.offer(line);
System.out.printf("%s inserted Line: %d\n",Thread.currentThread().getName(),buffer.size());
lines.signalAll() ; //缓冲区有数据的时候唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock(); //释放锁
}
}
/**
*
* 作者:fcs
* 描述:返回缓冲区的第一个字符串,先获取锁,然后检查缓冲区是不是有数据行,如果缓冲区是空的
* 就调用条件lines的await()方法等待缓冲区出现数据。
* 当其他线程调用条件lines的signal()或者signalAll()的时候,该线程唤醒
* 在有数据的时候,get方法获取缓冲区的第一行,并且调用条件space的signalAll()的方法,并且返回这个数据行字符串
* 说明:
* 返回:
* 参数:
* 时间:2015-4-15
*/
public String get(){
String line = null;
lock.lock();
try {
while((buffer.size() == 0) && hasPendingLines()){ //这里当条件是 <span style="font-family: Arial, Helvetica, sans-serif;">buffer.size() == 0) & ||hasPendingLines()就会发生死锁,类型是相互等待对方释放锁。</span>
lines.await();
}
if(hasPendingLines()){
line = buffer.poll();
System.out.printf("%s LIne readed: %d\n",Thread.currentThread().getName(),buffer.size());
space.signalAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
return line;
}
/*设置pendingLines的值的,当生产者不再生产新数据行的时候调用该方法*/
public void setPendingLines(boolean pendingLines){
this.pendingLines = pendingLines;
}
/*如果有数据行返回true*/
public boolean hasPendingLines(){
return pendingLines || buffer.size() > 0;
}
}
package cn.fans.chapter2.eight;
import java.util.Random;
/**
*
* @author fcs
* @date 2015-4-15
* 描述:消费者线程
* 说明:
*/
public class Consumer implements Runnable {
private Buffer buffer;
public Consumer(Buffer buffer) {
super();
this.buffer = buffer;
}
@Override
public void run() {
while(buffer.hasPendingLines()){
String line = buffer.get();
processLine(line);
}
}
//休眠10毫秒,模拟数据处理
public void processLine(String line){
Random random = new Random();
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package cn.fans.chapter2.eight;
/**
*
* @author fcs
* @date 2015-4-15
* 描述:生产者线程
* 说明:
*/
public class Producer implements Runnable{
private FileMock fileMock;
private Buffer buffer;
public Producer(FileMock fileMock, Buffer buffer) {
super();
this.fileMock = fileMock;
this.buffer = buffer;
}
/**
* 用来读取文件中的所有行,并且使用insert方法将数据插入到缓冲区中
* 读完数据后设置pendingLines方法通知缓冲区停止生产更多的行。
*/
@Override
public void run() {
buffer.setPendingLines(true);
while(fileMock.hasMoreLines()){
String line = fileMock.getLine();
buffer.insert(line);
}
buffer.setPendingLines(false);
}
}
package cn.fans.chapter2.eight;
public class Main {
public static void main(String[] args) {
FileMock fileMock = new FileMock(100, 10);
Buffer buffer = new Buffer(20);
Producer producer = new Producer(fileMock, buffer);
Thread pthread = new Thread(producer,"producer");
Consumer consumer [] = new Consumer[3];
Thread cthread [] = new Thread[3];
for(int i =0;i< 3;i++){
consumer[i] = new Consumer(buffer);
cthread[i] = new Thread(consumer[i]);
}
pthread.start();
for(int i =0;i< 3;i++){
cthread[i].start();
}
}
}
2.8在锁中使用多个条件(Multiple Condition) ---->生产者--消费者问题,使用锁和Condition条件实现,加入数据缓冲区
1.一个锁可能关联一个或者多个条件,这些条件通过Condition接口声明,目的是允许线程获取锁并且查看等待的某个条件是否满足,如果不满足
就挂起直到某个线程唤醒它们。
2.Condition接口提供了挂起线程和唤醒线程的机制。
3.本节使用一个数据缓冲区当做生产者生产数据的容器,消费者从该缓冲区获得数据。
4.与锁绑定的所有条件对象都是通过Lock接口声明的newCondition方法创建,返回一个Condition对象。
5.在使用条件的时候,必须获取该条件绑定的锁,带条件的代码必须在调用Lock对象的lock()方法和unlock()方法之间。
6.Condition接口提供了多个方法用来休眠和唤醒线程。
线程调用await()方法时,该线程自动释放这个条件绑定的锁,其他某个线程才可以获取这个锁并且执行相同的操作,或者执行这个锁保护的另一个临界区代码
7.必须在while循环中调用await()方法,在条件成立之前不能离开这个循环,当条件不成立时再次调用该方法。
8.signal()方法用来唤醒在某个条件成立的时候在等待锁的线程,并且该方法与await()方法是成对出现的。
9.如果调用了一个条件的await()方法,却没有调用signal()方法这个线程将永远休眠。
10.在本案例中修改while循环的条件将导致死锁。
2.9Condition条件的方法说明
1.await(long time,TimeUnit unit):该方法将等待一定的时间直到发生以下情况之前,线程一直处于休眠状态
1.其他某个线程中断当前线程。
2.其他某个线程调用了将当前线程挂起的条件的signal()方法或者signalAll()方法,就是提前唤醒。
3.指定的等待时间已经过去,自己自动醒来。
4.通过TimeUnit类的常量指定等待的时间。
2.awaitUninterruptibly():是不可中断的。这个线程将休眠直到其他某个线程调用将它挂起的条件的signal()或者signalAll()方法
3.awaitUnit(Date date)调用该方法的线程休眠和唤醒的时机与方法1相同。