第一題:現有的程序代碼模擬產生了16個日志對象,並且需要運行16秒才能打印完這些日志,請在程序中增加4個線程去調用parseLog()方法來分頭打印這16個日志對象,程序只需要運行4秒即可打印完這些日志對象。原始代碼如下:

交給四個線程,打印16個日志對象信息,啟動四個線程容易,但是怎樣將這16個日志對象交給4個線程,這時候我們用阻塞隊列,將要打印的日志信息放到阻塞隊列中,四個線程啟動時都從阻塞隊列中取數據,取完后打印即可。阻塞隊列的大小可以自行設置。
修改為多線程打印日志信息后的代碼及注釋參考下文:
1 public class Test { 2 public static void main(String[] args) { 3 System.out.println("begin:" + (System.currentTimeMillis() / 1000)); 4 final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(16); 5 //啟動4個線程 6 for(int i = 0; i < 4; i++){ 7 new Thread(new Runnable(){ 8 @Override 9 public void run() { 10 //不斷從阻塞隊列中取出數據並打印 11 while(true){ 12 String log; 13 try { 14 log = queue.take(); 15 parseLog(log); 16 } catch (Exception e) { 17 e.printStackTrace(); 18 } 19 } 20 } 21 22 }).start(); 23 } 24 for (int i = 0; i < 16; i++) { // 這行代碼不能改動 25 final String log = "" + (i + 1);// 這行代碼不能改動,生成的日志信息 26 { 27 try { 28 queue.put(log); //將日志信息放入阻塞隊列 29 //Test.parseLog(log); //打印日志信息 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } 33 } 34 } 35 } 36 // parseLog方法內部的代碼不能改動 37 public static void parseLog(String log) { 38 System.out.println(log + ":" + (System.currentTimeMillis() / 1000)); 39 try { 40 Thread.sleep(1000); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 } 45 }
第二題:現成程序中的Test類中的代碼在不斷地產生數據,然后交給TestDo.doSome()方法去處理,就好像生產者在不斷地產生數據,消費者在不斷消費數據。請將程序改造成有10個線程來消費生成者產生的數據,這些消費者都調用TestDo.doSome()方法去進行處理,故每個消費者都需要一秒才能處理完,程序應保證這些消費者線程依次有序地消費數據,只有上一個消費者消費完后,下一個消費者才能消費數據,下一個消費者是誰都可以,但要保證這些消費者線程拿到的數據是有順序的。原始代碼如下:

解答:
10個線程,依次消費數據,仍然是將待消費數據放入阻塞隊列,讓10個線程去取數據消費,所不同的是這次消費必須一個一個線程來,而不再是10個線程一起去取,因此用到了線程的同步。同步方法有多種,這里使用Semaphore來進行線程間的同步,代碼及注釋如下:
1 public class Test { 2 3 public static void main(String[] args) { 4 5 final SynchronousQueue<String> queue = new SynchronousQueue<String>(); 6 final Semaphore semaphore = new Semaphore(1); 7 //10個線程,分別消費數據,依舊是從阻塞隊列中獲取數據 8 for(int i=0; i < 10; i++){ 9 new Thread(new Runnable(){ 10 @Override 11 public void run() { 12 try { 13 semaphore.acquire(); 14 String input = queue.take(); 15 String output = TestDo.doSome(input); 16 System.out.println(Thread.currentThread().getName()+ ":" + output); 17 semaphore.release(); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } 21 } 22 23 }).start(); 24 } 25 26 System.out.println("begin:"+(System.currentTimeMillis()/1000)); 27 for(int i=0;i<10;i++){ //這行不能改動 28 String input = i+""; //這行不能改動,不斷產生數據 29 try { 30 queue.put(input); 31 } catch (Exception e) { 32 e.printStackTrace(); 33 } 34 //String output = TestDo.doSome(input);//不斷消費數據 35 //System.out.println(Thread.currentThread().getName()+ ":" + output); 36 } 37 } 38 }
