线程高级应用-心得9-空中网的三道面试题,考察应试者的线程掌握的深度

  1 1. 空中网面试题1
  2 
  3 package com.kongzhongwang.interview;
  4 
  5 import java.util.concurrent.ArrayBlockingQueue;
  6 import java.util.concurrent.BlockingQueue;
  7 
  8 //myeclipse强大功能:将代码直接复制到项目的src路径下可以自动相应生成包名和类名
  9 /**
 10  * 
 11  *    空中网面试题1:现有程序代码模拟产生16个日志对象,并且需要运行16秒才能打印完这些日志,请在程序中增加四个线程去调用
 12  * parseLog()方法来分头打印这16个日志对象,程序只需运行4秒即可打印完这些日志对象。
 13  * 考察新技术BlockingQueue
 14  */
 15 
 16 public class ReadLog {
 17     public static void main(String[] args) {
 18 
 19         /*此处有一个巧合:这里ArrayBlockingQueue<String>(1)和ArrayBlockingQueue<String>(16)
 20          * 达到的效果一样,并且前者产生的数据组合更整齐;目前推测是巧合,希望大牛发现因果了告知一声
 21          */
 22         final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1);
 23         for (int i = 0; i < 4; i++) {
 24             new Thread(new Runnable() {
 25                 public void run() {
 26                     while (true) {
 27                         try {
 28                             String log = queue.take();
 29                             parseLog(log);
 30                         } catch (InterruptedException e) {
 31                             e.printStackTrace();
 32                         }
 33                     }
 34                 }
 35             }).start();
 36         }
 37         System.out.println("begin:" + (System.currentTimeMillis() / 1000));
 38         /*
 39          * 模拟处理16个日志,下面代码产生了16个日志对象;当前代码需要运行16秒才能打印完成这些日志对象;
 40          * 修改程序代码,开四个线程让16个对象在4秒内打完
 41          */
 42         for (int i = 0; i < 16; i++) { // 这行代码不能改动
 43             final String log = "" + (i + 1); // 这行代码不能改动
 44             {
 45                 // ReadLog.parseLog(log);
 46                 try {
 47                     queue.put(log);
 48                 } catch (InterruptedException e) {
 49                     e.printStackTrace();
 50                 }
 51             }
 52         }
 53     }
 54 
 55     // parseLog内部代码不能动
 56     public static void parseLog(String log) {
 57         System.out.println(log + ":" + System.currentTimeMillis() / 1000);
 58         try {
 59             Thread.sleep(1000);
 60         } catch (InterruptedException e) {
 61             e.printStackTrace();
 62         }
 63     }
 64 }
 65 2. 空中网面试题2
 66 
 67 package com.kongzhongwang.interview;
 68 
 69 import java.util.concurrent.Semaphore;
 70 import java.util.concurrent.SynchronousQueue;
 71 
 72 /**
 73  *    空中网面试题2: 现成程序中的Test类中的代码在不断地产生数据,然后交给TestDo.doSome()方法去处理;
 74  * 这就好像是生产者在不断地产生数据,消费者在不断地消费数据。请将程序改造成有10个线程来消费生产者产生的数据,
 75  * 这些消费者都调用TestDo.doSome()方法去处理,固每个消费者都需要1秒才能处理完,程序应该保证这些
 76  * 消费者线程依次有序的消费数据,只有上一个消费者消费完后,下一个消费者才能消费数据,下一个消费者是谁都可以, 但要保证消费者拿到的数据是有顺序的。
 77  */
 78 public class Test {
 79 
 80     public static void main(String[] args) {
 81 
 82         //使用semaphore信号灯相当于上一个lock锁
 83         final Semaphore semaphore = new Semaphore(1);
 84         //新的队列方式
 85         final SynchronousQueue<String> queue = new SynchronousQueue<String>();
 86         for(int i=0;i<10;i++){
 87             new Thread(new Runnable() {
 88                 
 89                 @Override
 90                 public void run() {
 91                     try {
 92                         semaphore.acquire();
 93                         String input = queue.take();
 94                         String output = TestDo.doSome(input);
 95                         System.out.println(Thread.currentThread().getName() + ":" + output);
 96                         semaphore.release();
 97                     } catch (InterruptedException e) {
 98                         e.printStackTrace();
 99                     }
100                 }
101             }).start();
102         }
103         System.out.println("begin:" + (System.currentTimeMillis() / 1000));
104 
105         for (int i = 0; i < 10; i++) { // 这行代码不能改动
106             String input = i + ""; // 这行代码不能改动
107             try {
108                 queue.put(input);
109             } catch (InterruptedException e) {
110                 e.printStackTrace();
111             }
112         }
113     }
114 }
115 
116 // TestDo类不能动
117 class TestDo {
118 
119     public static String doSome(String input) {
120         try {
121             Thread.sleep(1000);
122         } catch (Exception e) {
123             e.printStackTrace();
124         }
125         String output = input + ":" + (System.currentTimeMillis() / 1000);
126         return output;
127     }
128 
129 }
130  3.空中网面试题3
131 
132 package com.kongzhongwang.interview;
133 
134 import java.util.ArrayList;
135 import java.util.Iterator;
136 import java.util.concurrent.CopyOnWriteArrayList;
137 
138 public class Tests extends Thread {
139 
140     /**
141      * 空中网面试题3: 现有程序同时启动了四个线程去调用TestDo.doSome(key,value)方法;
142      * 由于TestsDo.doSome(key,value)方法内的代码是先暂停1秒,然后再输出以秒为单位的当前时间值,
143      * 所以会打印出四个相同的时间值,如下所示:4:4 1258199615 1:1 1258199615 3:3 1258199615 2:2
144      * 1258199615 ;请修改代码,如果有几个线程调用TestDo.doSome(key,value)方法时;
145      * 传递进去的key值相等(equals比较为true),则这几个线程应互斥输出结果,即当有两个线程的key都为1时,
146      * 它们中的一个要比其他线程晚一步输出结果,如下所示:4:4 1258199615 1:1 1258199615 3:3 1258199615 1:2
147      * 1258199616 ;总之每个线程中指定的key相等时;这些相等的线程应每隔1秒输出时间值(要用互斥),
148      * key不同,则并行执行(相互之间不互斥)
149      */
150 
151     private TestsDo testDo;
152     private String key;
153     private String value;
154 
155     private Tests(String key, String key2, String value) {
156         this.testDo = TestsDo.getInstance();
157         /*
158          * 常量“1”和“1”是同一个对象,下面这行代码就是要用“1”+“”的方式产生新的对象;
159          * 以实现内容没有改变,仍然相等(都还为“1”),但对象却不再是同一个的效果
160          */
161         this.key = key + key2;
162         /*
163          * a = "1"+"";
164          * b = "2"+"";
165          * a和b是同一个对象,因为编译器在执行之前就会将其优化为 a=“1”;
166          * 但是this.key = key + key2;这句,编译器不会给你优化,
167          * 因为你是属性变量,编译器不知道你将来要传入什么值
168          */
169         this.value = value;
170     }
171 
172     public static void main(String[] args) {
173 
174         Tests a = new Tests("1", "", "1");
175         Tests b = new Tests("1", "", "2");
176         Tests c = new Tests("3", "", "3");
177         Tests d = new Tests("4", "", "4");
178         System.out.println("begin:" + (System.currentTimeMillis() / 1000));
179         a.start();
180         b.start();
181         c.start();
182         d.start();
183     }
184 
185     public void run() {
186         testDo.doSome(key, value);
187     }
188 }
189 
190 class TestsDo {
191     private TestsDo() {}
192     private static TestsDo _instance = new TestsDo();
193     public static TestsDo getInstance() {
194         return _instance;
195     }
196     //传统写法,没有考虑到线程并发问题    
197 //    private ArrayList keys = new ArrayList();
198     private CopyOnWriteArrayList keys = new CopyOnWriteArrayList();
199     public void doSome(Object key,String value){
200         Object o = key;
201         if(! keys.contains(o)){
202             keys.add(o);
203         }else{
204             //迭代的过程中不能进行其他操作;
205             for(Iterator iter = keys.iterator();iter.hasNext();){
206                 /*这里的休眠作用:为了让大家看到,使用传统的private ArrayList keys = new ArrayList();
207                  * 会导致Exception in thread "Thread-1" java.util.ConcurrentModificationException异常
208                  * 因为迭代的过程中不能进行其他操作;你非要在迭代的时候向其中添加数据就会导致这种异常,而且在迭代中放入休眠这种错误百发百中。
209                  */
210                 try {
211                     Thread.sleep(20);
212                 } catch (InterruptedException e) {
213                     e.printStackTrace();
214                 }
215                 Object oo = iter.next();
216                 if(o.equals(oo)){
217                 o = oo;    
218                 }
219             }
220         }
221         //这里为了区别是不同对象,所以不能直接使用synchronized(key)
222         synchronized(o)
223         //大括号内的是需要同步的代码,不能改动
224         {
225             try{
226                 Thread.sleep(1000);
227                 System.out.println(key+":"+value+":" + (System.currentTimeMillis() / 1000));
228             }catch(Exception e){
229                 e.printStackTrace();
230             }
231         }
232     }
233 }
原文地址:https://www.cnblogs.com/cxxjohnson/p/6261907.html