一道多线程笔试题目的联想
2015-08-10 11:52
369 查看
题目描述
一个文件中有10000个数,用Java实现一个多线程程序将这个10000个数输出到5个不用文件中(不要求输出到每个文件中的数量相同)。要求启动10个线程,两两一组,分为5组。每组两个线程分别将文件中的奇数和偶数输出到该组对应的一个文件中,需要偶数线程每打印10个偶数以后,就将奇数线程打印10个奇数,如此交替进行。同时需要记录输出进度,每完成1000个数就在控制台中打印当前完成数量,并在所有线程结束后,在控制台打印”Done”.
思路
首先,抓住题目的重点,就是如何让两个线程(奇偶)交替干活。我们学过的线程调度的方法:(1)sleep/interrupt,为了实现两个奇偶线程交替写入,我们必须给两个线程使用同一个对象锁,但是线程sleep之后,只是让出CPU资源,但并不释放对象锁,也就是在一个线程休眠之后,另一个线程还是无法写入。如果不加锁,线程休眠无法准确保证线程的调用顺序。(2)设置线程优先级,与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。 (3)让步。线程的让步含义就是使当前运行着线程让出CPU资源,但是然给谁不知道,仅仅是让出,线程状态回到可运行状态。可能让步完,自己线程接着进入运行状态。(4)合并。线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。 也就是说如果两个线程互相调用对方的join方法来实现交替是什么结果呢?看测试结果:
运行结果是:
结果是某个线程运行一次之后就死锁了。
总结上面错误的思路之后,就是应了那句老话:不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。
个人解题思路
到这里,思路差不多就出来了,使用锁+wait/notify机制。
(1)为了保证两个线程能交替干活,给两个线程加上同一个对象锁,这样即使一个线程写数据写到一半退出CPU,但是我还是继续持有该对象锁,另一线程也不能执行写入操作,进而保证了可以交替写入。(2)wait/notify可以保证一个线程在完成写入操作之后,唤醒该对象锁上等待的线程(也就是对面那个线程),同时让自己线程在 该对象监视器上进入等待状态,知道对面线程将其唤醒。
注:wait和sleep区别是,两者都会暂停当前线程,释放CPU控制权,但是wait还会释放对象锁的控制;还要注意的是:notify()调用之后,并不会立即释放对象锁,而是在相应的synchronized(){}语句执行完后,自动释放锁,JVM会在wait对象锁的线程中随机选取一个线程,赋予其对象锁,唤醒线程,继续执行。
代码演示(为了方便调试,将题目的数量级降到原来的1/10):
至于题目要求的五组线程,只需要使用五个不同的对象锁即可。
一个文件中有10000个数,用Java实现一个多线程程序将这个10000个数输出到5个不用文件中(不要求输出到每个文件中的数量相同)。要求启动10个线程,两两一组,分为5组。每组两个线程分别将文件中的奇数和偶数输出到该组对应的一个文件中,需要偶数线程每打印10个偶数以后,就将奇数线程打印10个奇数,如此交替进行。同时需要记录输出进度,每完成1000个数就在控制台中打印当前完成数量,并在所有线程结束后,在控制台打印”Done”.
思路
首先,抓住题目的重点,就是如何让两个线程(奇偶)交替干活。我们学过的线程调度的方法:(1)sleep/interrupt,为了实现两个奇偶线程交替写入,我们必须给两个线程使用同一个对象锁,但是线程sleep之后,只是让出CPU资源,但并不释放对象锁,也就是在一个线程休眠之后,另一个线程还是无法写入。如果不加锁,线程休眠无法准确保证线程的调用顺序。(2)设置线程优先级,与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。 (3)让步。线程的让步含义就是使当前运行着线程让出CPU资源,但是然给谁不知道,仅仅是让出,线程状态回到可运行状态。可能让步完,自己线程接着进入运行状态。(4)合并。线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。 也就是说如果两个线程互相调用对方的join方法来实现交替是什么结果呢?看测试结果:
//偶数线程 public void run() { while (step <= 50) { synchronized (readNum) { try { readEvenNum(br, bw); step++; System.out.println("偶数读了10个,让给奇数。。。。"+step); oddThread.join(); } catch (Exception e) { e.printStackTrace(); } } } } //奇数线程 public void run() { while (step <= 50) { synchronized (readNum) { try { readOddNum(br, bw); step++; System.out.println("奇数读了10个,让给偶数。。。。"+step); evenThread.join(); } catch (Exception e) { e.printStackTrace(); } } } }
运行结果是:
结果是某个线程运行一次之后就死锁了。
总结上面错误的思路之后,就是应了那句老话:不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。
个人解题思路
到这里,思路差不多就出来了,使用锁+wait/notify机制。
syn(obj){ obj.read(); obj.notify(); obj.wait(); }
(1)为了保证两个线程能交替干活,给两个线程加上同一个对象锁,这样即使一个线程写数据写到一半退出CPU,但是我还是继续持有该对象锁,另一线程也不能执行写入操作,进而保证了可以交替写入。(2)wait/notify可以保证一个线程在完成写入操作之后,唤醒该对象锁上等待的线程(也就是对面那个线程),同时让自己线程在 该对象监视器上进入等待状态,知道对面线程将其唤醒。
注:wait和sleep区别是,两者都会暂停当前线程,释放CPU控制权,但是wait还会释放对象锁的控制;还要注意的是:notify()调用之后,并不会立即释放对象锁,而是在相应的synchronized(){}语句执行完后,自动释放锁,JVM会在wait对象锁的线程中随机选取一个线程,赋予其对象锁,唤醒线程,继续执行。
代码演示(为了方便调试,将题目的数量级降到原来的1/10):
package wangyi; import java.io.*; public class ReadNum { public static void main(String[] args) throws Exception { File file = new File("D:\\readNum\\num.txt"); File file1 = new File("D:\\readNum\\out1\\out1.txt"); ReadNum readNum = new ReadNum(); EvenThread evenThread = new EvenThread(file, file1, readNum); OddThread oddThread = new OddThread(file, file1, readNum); evenThread.start(); oddThread.start(); } } class EvenThread extends Thread { FileReader fis; FileWriter fos; BufferedReader br; BufferedWriter bw; ReadNum readNum; int step = 0; public EvenThread(File file, File file1, ReadNum readNum) throws Exception { this.fis = new FileReader(file); this.fos = new FileWriter(file1,true); this.br = new BufferedReader(fis); this.bw = new BufferedWriter(fos); this.readNum = readNum; } public void run() { while (step <= 50) { synchronized (readNum) { try { readEvenNum(br, bw); step++; readNum.notify(); readNum.wait(); } catch (Exception e) { e.printStackTrace(); } } } } public static void readEvenNum(BufferedReader br, BufferedWriter bw) throws IOException { int count = 0; while (count < 10) { String str = br.readLine(); if(str==null){ System.out.print(str+" "); }else{ if (Integer.parseInt(str) % 2 == 0) { bw.write(str); bw.write(" "); bw.flush(); count++; } } } } } class OddThread extends Thread { FileReader fis; FileWriter fos; BufferedReader br; BufferedWriter bw; ReadNum readNum; int step = 0; public OddThread(File file, File file1, ReadNum readNum) throws Exception { this.fis = new FileReader(file); this.fos = new FileWriter(file1,true); this.br = new BufferedReader(fis); this.bw = new BufferedWriter(fos); this.readNum = readNum; } public void run() { while (step <= 50) { synchronized (readNum) { try { readOddNum(br, bw); step++; if (step % 5 == 0) { System.out.println("已完成打印数量:" + step * 20); if (step == 50) { System.out.println("Done!"); } } readNum.notify(); readNum.wait(); } catch (Exception e) { e.printStackTrace(); } } } } public static void readOddNum(BufferedReader br, BufferedWriter bw) throws IOException { int count = 0; while (count < 10) { String str = br.readLine(); if(str==null){ System.out.print(str+" "); }else{ if (Integer.parseInt(str) % 2 == 1) { bw.write(str); bw.write(" "); bw.flush(); count++; } } } } }
至于题目要求的五组线程,只需要使用五个不同的对象锁即可。
相关文章推荐
- Ubuntu下搜狗输入的安装
- 杂谈_我希望在软件开发生涯初期就知道的 4 件事
- 一个字符串有两个A,计算里面的字符个数,并打印出来
- Go-下载网上图片
- 医院病床分配仿真 举例(c语言)
- 探索React生态圈
- 两个数的乘积等于其最大公约数与最小公倍数的乘积,怎么证明?
- 获取URL对应的资源
- 1047. Student List for Course (25)
- 最长公共子序列(LCS)
- IOS开发之多线程详解
- sublime text主题预览
- 前往架构师的路上不得不知道的知识(1)——编程语言的概念
- 为什么放弃WebView
- Hotel
- 网站区别手机端和pc端用HttpContext.Current.Request.Url
- Command /bin/sh failed with exit code 64问题排查
- 欧拉函数
- 超详细!iOS 并发编程之 Operation Queues
- 回调函数学习笔记