【正确答案】本题考查的是对多线程编程的理解。为了便于理解,首先用随机函数随机生成10000个数放到文件中,以供测试使用。一次把这10000条记录读到内存中,平均分配给5组线程并行处理,因此,本题的难点是如何控制打印偶数的线程和打印奇数的线程轮流运行。
本题通过Java提供的Condition来实现线程的同步。Condition是在Java 1.5中才出现的,它用来替代传统的Object类的wait()、notify()方法,以实现线程间的协作,相比使用Object类的wait()、nofify()方法,使用Condition的await()、signal()这种方式实现线程间协作,更加安全和高效。它主要有如下特点:
1)Condition最常用的方法为await()和signal(),其中,await()对应Object类的wait()方法,signal()对应Object类的notify()方法。
2)Condition依赖于Lock接口,生成一个Condition的代码为lock.newCondition()。
3)调用Condition的await()和signal()方法必须在lock保护之内。
对于本题而言,定义两个Condition(oddLock和evenLock),首先打印奇数的线程开始运行,通过调用evenLock.await()来等待打印偶数的线程执行。接着打印偶数的线程开始运行,当输出10个偶数或者没有偶数输出后,调用evenLock.signal()来通知打印奇数的线程开始运行,然后调用oddLock.wait方法来等待打印奇数的线程运行完成。通过这种方法来控制奇数线程与偶数线程的运行顺序,实现代码如下:
import java.io.*;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test
{
private static final int count=10000;
private static final int threadGruopCount=5;
private static final String inputFile="testInput.txt";
public static void generateTestFile()throws IOException
{
//用随机数生成10000个测试数据放到文件中
PrintWriter pw=new PrintWriter(new FileWriter(new File(inputFile)),true);
Random random=new Random();
for(inti=0;i<count;i++)
{
pw.write(Math.abs(random.nextInt())%count+",");
}
pw.flush();
pw.close();
}
public static void main(String[]args)
{
try
{
generateTestFile();
BufferedReader reader=new BufferedReader(new FileReader(inputFile));
String str=reader.readLine();
reader.close();
String[]strs=str.split(",");
int index=0;
//为了简单,每个文件输出数字的个数相同
int countForEachFile=count/threadGmopCount;
for(int i=0;i<threadGmopCount;i++)
{
int records[]=new int[countForEachFile];
for(int j=0;j<countForEachFile;j++)
{
records[j]=Integer.parseInt(strs[index]);
index++;
}
PrintGroup group=new PrintGroup(records,i);
group.startPrint();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
class PrintGroup{
//这个线程组输出数字的个数
private static volatile int count=0;
private Lock lock=new ReentrantLock();
private Condition oddLock=lock.newCondition();
private Condition evenLock=lock.newCondition();
//这个线程组需要输出的数字数组
private int records[];
//这个线程组需要把数字输出到同一个文件,因此,共享一个writer
//由于任意时刻只会有一个线程写文件,因此,不需要同步
private PrintWriter writer;
//记录输出奇数所在的数组下标
private volatile int oddIndex=0;
//记录输出偶数所在的数组下标
private volatile im evenlndex=0;
//输出奇数的线程
private OddPrintThread oddPrintThread;
//输出偶数的线程
private EvenPrintThread evenPrintThread;
private volatile boolean first=true;
private int[] result=new int[2000];
private int index=0;
public PrintGroup(int[] records, int id) throws Exception {
this.records=records;
this.writer=new PrintWriter(new FileWriter(new File("output" + id + ".txt")), true);
}
public void startPrint() {
oddPrintThread=new OddPrintThread();
evenPrintThread=new EvenPrintThread();
oddPrintThread.start();
evenPrintThread.start();
}
private class OddPrintThread extends Thread
{
@Override
public void run()
{
while (true)
{
try{
lock.lock();
if(first)//第一次运行时,需要等待打印偶数的线程先执行
{
first=false;
evenLock.await();
}
for (int i=0; i<10;)
if(oddIndex>=records.length&& evenIndex>=records.length)
{
writer.flush();
writer.close();
return;
}
//如果所有事物奇数都打印完了,则不打印奇数,让打印偶数的线程有
//运行机会
if (oddlndex>=records.length )
{
break;
}
if(records[oddIndex]%2==1)
{
i++;
writer.print(records[oddlndex] + " ");
result[index++]=records[oddlndex];
writer.flush();
addCount();
}
oddIndex++;
}
//打印完10个奇数后,通知打印偶数的线程开始运行
oddLock.signal();
//接着等待打印偶数的线程结束
evenLock.await();
}
catch(Exception e)
{
e.printStackTrace{};
}
finally
{
oddLock.signal();
lock.unlock();
}
}
}
}
private class EvenPrintThread extends Thread{
@Override
public void rum()
{
while(true)
{
try{
//等待打印奇数的线程先运行。如果这个线程先运行调用evenLock.signal();
//然后打印奇数线程才开始运行,打印奇数线程会通过调用evenLock.await();
//进入休眠状态,此时打印奇数线程将永远不会被唤醒
while(first){
Thread.sleep(1);
}
lock.lock();
for(int i=0;i<10;)
{
if(oddIndex>=records.length&&evenIndex>=records.length)
{
String s="";
for(int k=0;k<2000;k++)
{
s+=(result[k]+" ");
}
writer.flush();
return;
}
if(evenlndex>=records.length)
{
break;
}
if(records[evenIndex]%2==0)
{
i++;
writer.print(records[everdndex]+" ");
result[index++]=records[evenIndex];
writer.flush();
addCount();
}
evenIndex++;
}
evenLock.signal();
oddLock.await();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
evenLock.signal();
lock.unlock();
}
}
}
}
private synchronized static void addCount()
{
count++;
if(count%1000==0)
{
System.out.println("已完成: "+count);
if(count==10000)
{
System.out.println("Done");
}
}
}
}
程序的运行结果为:
己完成:1000
己完成:2000
已完成:3000
已完成:4000
已完成:5000
已完成:6000
己完成:7000
已完成:8000
已完成:9000
已完成:10000
Done
【答案解析】