-如果限定只用一个线程读文件,其他三个线程处理文本,你要怎么做。
-我想这个问题可以采用与读者写者问题类似的方法来解决。具体应该可以用以下几种方式实现:
不上锁、不使用信号量的话,可以直接设置状态量,判断是否在读取、计算字符、计算行数等,例如开始时设isRead为true,读取完文件后,设为false;其他部分检测到为false,开始执行各自的计数操作。这与读取完文件后再发送通知其实类似。
或者可以设置信号量,读者和写者间设置mutex1,互斥地进行读取、计数操作;读者间设置mutex2,互斥地访问读者计数量。
Java中还有一个重入锁ReentrantLock,可以设置readLock和writeLock,在只有一个写操作时使读操作并行,也可以完成需要的功能。
对于上面提出的三个方法:状态量、信号量和重入锁,我进行了简化实现。
状态量
设置一个状态量isRead,判断是否在进行读入操作,初始值为true。当读入操作结束后设为false,开始进行计数操作。这种方式限于只需第一次读入的情况。以下是部分主要代码:
static class fileReader implements Runnable {
public void run() {
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
int in = 0;
try {
inputStreamReader = new InputStreamReader
(new FileInputStream("input.txt"));
if (inputStreamReader != null) {
bufferedReader = new BufferedReader(inputStreamReader);
}
while ((in = bufferedReader.read()) != -1) {
file.append((char) in);
}
//设置标志,完成读入
isRead = false;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
static class countChar implements Callable<Long> {
public Long call() {
int i = 0;
while (1 > 0) {
if (!isRead) {
while (i < file.length()) {
charNum++;
i++;
}
break;
}
}
return charNum;
}
}
信号量
设置一个同步信号量和两个互斥信号量,同步信号用于确保第一次操作为读入操作,两个互斥信号用于完成“读者写者”,即读入部分与计数部分互斥,计数部分间不互斥。这种方法经过简单修改就可实现多次读入多次计数。以下是部分主要代码:
static class fileReader implements Runnable {
public void run() {
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
int in = 0;
try {
//进行读入时不允许计数
rmutex.acquire();
inputStreamReader = new InputStreamReader
(new FileInputStream("input.txt"));
if (inputStreamReader != null) {
bufferedReader = new BufferedReader(inputStreamReader);
}
while ((in = bufferedReader.read()) != -1) {
file.append((char) in);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
rmutex.release();
//完成一次读入后才能进行计数
vmutex.release(2);
}
}
}
static class countChar implements Callable<Long> {
public Long call() {
int i = 0;
while (1 > 0) {
try {
//允许计数
vmutex.acquire();
cmutex.acquire();
//进行计数时不允许读入
if (count==0) {
rmutex.acquire();
}
//添加“读者”
count++;
cmutex.release();
while (i < file.length()) {
charNum++;
i++;
}
cmutex.acquire();
//完成“阅读”
count--;
if (count==0) {
rmutex.release();
}
cmutex.release();
break;
} catch (Exception e) {
e.printStackTrace();
}
}
return charNum;
}
}
重入锁
设置一个读写锁和一个同步信号量,同步信号用于确保第一次操作为读入操作,读写锁用于完成“读者写者”,即读入部分与计数部分互斥,计数部分间不互斥。同样地,这种方法经过简单修改就可实现多次读入多次计数,而且还具有降级功能(写锁可降级为读锁)。以下是部分主要代码:
static class fileReader implements Runnable {
public void run() {
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
int in = 0;
try {
inputStreamReader = new InputStreamReader
(new FileInputStream("input.txt"));
if (inputStreamReader != null) {
bufferedReader = new BufferedReader(inputStreamReader);
}
//加上写锁,不允许其他进程写或读
lock.writeLock().lock();
while ((in = bufferedReader.read()) != -1) {
file.append((char) in);
}
//释放写锁
lock.writeLock().unlock();
//允许计数
vmutex.release(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
static class countChar implements Callable<Long> {
public Long call() {
int i = 0;
while (1>0) {
try {
//允许计数
vmutex.acquire();
//加上读锁,其他进程可读不可写
lock.readLock().lock();
while (i < file.length()) {
charNum++;
i++;
}
//释放读锁
lock.readLock().unlock();
break;
} catch (Exception e) {
e.printStackTrace();
}
}
return charNum;
}
}
性能比较
随机生成一千万次单词数据,经测试,三者时间相近,总时间都在1500ms左右,由此可知在基础的应用中三种方法基本没有性能差异。