【Java多线程与并发库】19.java线程面试题2
2016-11-11 09:54
756 查看
现有的Test类中代码在不断的产生数据,然后交给TestDo.doSome()方法去处理,就好像生产者
在不断地产生数据,消费者在不断消费数据。请将程序改造成有10个线程来消费生成者产生的数据,
这些消费者都调用TestDo.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序
应该保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能
消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。原始代码
如下:
分析:
题目中要求不光要产生10个线程去消费数据,而且要求有序的执行消费,并且消费数据是有顺序
的,这不就是典型的“先入先出”队列吗?使用阻塞队列的话,还可以保证绝对的顺序。下面
是实现代码:
我开了十个线程等待从阻塞队列中拿出数据来进行操作,只要放入数据,那边线程就会取出数据,
然后进行打印。
效果:
但是这个代码有个致命的漏洞,就是没有保证这些线程“拿到的数据是有顺序的”。但是我们
实现了一秒钟解决十秒钟的工作量,但是仔细读题,人家不要求你10个线程让秒内完成,人家
允许“每个消费者都需要一秒才能处理完”,所以,我们要进行互斥操作来使每次从队列中取出的
数据是有顺序的,于是乎我对取出操作和打印操作都加了线程锁:
效果:
这样就实现了按照顺序打印,可是大家可以看到,我们只使用了十个线程里面的3个,就不满足
题目说的十个线程都去执行打印操作的要求,所以上面这个代码还是不成立的。
其实上面的代码只需改动一处就可以实现题目要求,就是把while循环去掉!这个东西一开始
就不应该加上的,因为线程会不停的检测,一旦满足条件就去执行,而去掉while循环之后,
其实每一个线程只能执行一次了,然后就可以将工作量平摊到10个线程身上去了。
修改后的代码:
PS:其实阻塞队列开1个空间就可以满足了,因为是加了线程锁,所以有双重阻塞,10个多余了。
效果:
完美解决方案:
祭出最后的大杀器----SynchronousQueue!这个队列也是阻塞队列,但是这个
队列有一个特点,就是只有在进行移除操作的时候,才能进行插入操作。这就符合了我们
这个需求,就是上一个做完了下一个再去插入。
然后辅助工具就是我们的----Semaphore信号灯,哪个线程去操作的时候,其它线程都不能抢占,
然后该线程做完所有工作之后,将信号灯释放,这个时候其它的线程才可以去操作。
以上就可以解决一个一个取,和按顺序取的两个问题。
代码:
效果:
这就实现了该题目要求的最终结果。
转载请注明出处:http://blog.csdn.net/acmman/article/details/53125959
在不断地产生数据,消费者在不断消费数据。请将程序改造成有10个线程来消费生成者产生的数据,
这些消费者都调用TestDo.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序
应该保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能
消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。原始代码
如下:
package cn.edu.hpu.test; public class DoSomeTest { public static void main(String[] args) { System.out.println("begin:"+(System.currentTimeMillis()/1000)); for (int i = 0; i < 10; i++) {//这行不能改动 String input=i+"";//这行不能改动 String output=TestDo.doSome(input); System.out.println(Thread.currentThread().getName()+":"+output); } } } //不能改动此TestDao类 class TestDo{ public static String doSome(String input){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String output=input+":"+(System.currentTimeMillis()/1000); return output; } }效果:
分析:
题目中要求不光要产生10个线程去消费数据,而且要求有序的执行消费,并且消费数据是有顺序
的,这不就是典型的“先入先出”队列吗?使用阻塞队列的话,还可以保证绝对的顺序。下面
是实现代码:
package cn.edu.hpu.test; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class DoSomeTest { public static void main(String[] args) { final BlockingQueue<String> queue=new ArrayBlockingQueue<String>(10); for (int i = 0; i < 10; i++) { new Thread(new Runnable(){ public void run() { String input=null; while(true){ if(queue.size()>0){ try { input=queue.take(); } catch (InterruptedException e) { e.printStackTrace(); } String output=TestDo.doSome(input); System.out.println(Thread.currentThread().getName()+":"+output); } } } }).start(); } System.out.println("begin:"+(System.currentTimeMillis()/1000)); for (int i = 0; i < 10; i++) {//这行不能改动 String input=i+"";//这行不能改动 try { queue.put(input); } catch (InterruptedException e) { e.printStackTrace(); } } } } //不能改动此TestDao类 class TestDo{ public static String doSome(String input){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String output=input+":"+(System.currentTimeMillis()/1000); return output; } }
我开了十个线程等待从阻塞队列中拿出数据来进行操作,只要放入数据,那边线程就会取出数据,
然后进行打印。
效果:
但是这个代码有个致命的漏洞,就是没有保证这些线程“拿到的数据是有顺序的”。但是我们
实现了一秒钟解决十秒钟的工作量,但是仔细读题,人家不要求你10个线程让秒内完成,人家
允许“每个消费者都需要一秒才能处理完”,所以,我们要进行互斥操作来使每次从队列中取出的
数据是有顺序的,于是乎我对取出操作和打印操作都加了线程锁:
package cn.edu.hpu.test; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class DoSomeTest { public static void main(String[] args) { final BlockingQueue<String> queue=new ArrayBlockingQueue<String>(10); final Lock lock=new ReentrantLock(); for (int i = 0; i < 10; i++) { new Thread(new Runnable(){ public void run() { String input=null; while(true){ if(queue.size()>0){ lock.lock(); try { input=queue.take(); String output=TestDo.doSome(input); System.out.println(Thread.currentThread().getName()+":"+output); } catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock(); } } } } }).start(); } System.out.println("begin:"+(System.currentTimeMillis()/1000)); for (int i = 0; i < 10; i++) {//这行不能改动 String input=i+"";//这行不能改动 try { queue.put(input); } catch (InterruptedException e) { e.printStackTrace(); } } } } //不能改动此TestDao类 class TestDo{ public static String doSome(String input){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String output=input+":"+(System.currentTimeMillis()/1000); return output; } }
效果:
这样就实现了按照顺序打印,可是大家可以看到,我们只使用了十个线程里面的3个,就不满足
题目说的十个线程都去执行打印操作的要求,所以上面这个代码还是不成立的。
其实上面的代码只需改动一处就可以实现题目要求,就是把while循环去掉!这个东西一开始
就不应该加上的,因为线程会不停的检测,一旦满足条件就去执行,而去掉while循环之后,
其实每一个线程只能执行一次了,然后就可以将工作量平摊到10个线程身上去了。
修改后的代码:
package cn.edu.hpu.test; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class DoSomeTest { public static void main(String[] args) { final BlockingQueue<String> queue=new ArrayBlockingQueue<String>(1); final Lock lock=new ReentrantLock(); for (int i = 0; i < 10; i++) { new Thread(new Runnable(){ public void run() { String input=null; try { lock.lock(); input=queue.take(); String output=TestDo.doSome(input); System.out.println(Thread.currentThread().getName()+":"+output); lock.unlock(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } System.out.println("begin:"+(System.currentTimeMillis()/1000)); for (int i = 0; i < 10; i++) {//这行不能改动 String input=i+"";//这行不能改动 try { queue.put(input); } catch (InterruptedException e) { e.printStackTrace(); } } } } //不能改动此TestDao类 class TestDo{ public static String doSome(String input){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String output=input+":"+(System.currentTimeMillis()/1000); return output; } }
PS:其实阻塞队列开1个空间就可以满足了,因为是加了线程锁,所以有双重阻塞,10个多余了。
效果:
完美解决方案:
祭出最后的大杀器----SynchronousQueue!这个队列也是阻塞队列,但是这个
队列有一个特点,就是只有在进行移除操作的时候,才能进行插入操作。这就符合了我们
这个需求,就是上一个做完了下一个再去插入。
然后辅助工具就是我们的----Semaphore信号灯,哪个线程去操作的时候,其它线程都不能抢占,
然后该线程做完所有工作之后,将信号灯释放,这个时候其它的线程才可以去操作。
以上就可以解决一个一个取,和按顺序取的两个问题。
代码:
package cn.edu.hpu.test; import java.util.concurrent.Semaphore; import java.util.concurrent.SynchronousQueue; public class DoSomeTest { public static void main(String[] args) { final Semaphore semaphore = new Semaphore(1); final SynchronousQueue<String> queue=new SynchronousQueue<String>(); for (int i = 0; i < 10; i++) { new Thread(new Runnable(){ public void run() { String input=null; try { semaphore.acquire();//占用信号灯 input=queue.take(); String output=TestDo.doSome(input); System.out.println(Thread.currentThread().getName()+":"+output); semaphore.release();//释放信号灯 } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } System.out.println("begin:"+(System.currentTimeMillis()/1000)); for (int i = 0; i < 10; i++) {//这行不能改动 String input=i+"";//这行不能改动 try { queue.put(input); } catch (InterruptedException e) { e.printStackTrace(); } } } } //不能改动此TestDao类 class TestDo{ public static String doSome(String input){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String output=input+":"+(System.currentTimeMillis()/1000); return output; } }
效果:
这就实现了该题目要求的最终结果。
转载请注明出处:http://blog.csdn.net/acmman/article/details/53125959
相关文章推荐
- 【Java多线程与并发库】18.java线程面试题1
- Java 多线程 并发 锁 Java线程面试题 Top 50
- 【Java多线程与并发库】20.java线程面试题3
- java多线程之线程并发库面试题
- Java面试题--多线程、并发及线程的基础问题
- java多线程与并发之java线程简介(二)
- 【java多线程与并发库】---传统java多线程<4> .线程状态及优先级
- 多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
- Java多线程面试题:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次, 接着再回到主线程又循环100,如此循环50次
- Java多线程编程--(10)学习Java5.0 并发编程包--线程工具类
- 多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
- 【java多线程与并发库】---传统java多线程<5> 线程控制
- JAVA多线程并发同步,以及线程终止
- java多线程与并发之java线程简介(六)
- 【java多线程与并发库】---传统java多线程<1>线程基本概念
- 多线程并发库高级应用 之 使用java5中同步技术的3个面试题
- 【java多线程与并发库】---传统java多线程<2> 线程创建方式
- (13)多线程与并发库之java5阻塞队列(BlockingQueue)的应用----子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程循环100次,如此循环50次
- 【java多线程与并发库】---传统java多线程<3> .线程分类
- 多线程并发库高级应用 之 java5中的线程并发库--线程锁技术