您的位置:首页 > 职场人生

【Java多线程与并发库】19.java线程面试题2

2016-11-11 09:54 756 查看
现有的Test类中代码在不断的产生数据,然后交给TestDo.doSome()方法去处理,就好像生产者

在不断地产生数据,消费者在不断消费数据。请将程序改造成有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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐