第一題:現有的程序代碼模擬產生了16個日志對象,並且需要運行16秒才能打印完這些日志,請在程序中增加4個線程去調用parseLog()方法來分頭打印這16個日志對象,程序只需要運行4秒即可打印完這些日志對象。原始代碼如下:
package read; public class Test { public static void main(String[] args){ System.out.println("begin:"+(System.currentTimeMillis()/1000)); /*模擬處理16行日志,下面的代碼產生了16個日志對象,當前代碼需要運行16秒才能打印完這些日志。 修改程序代碼,開四個線程讓這16個對象在4秒鍾打完。 */ for(int i=0;i<16;i++){ //這行代碼不能改動 final String log = ""+(i+1);//這行代碼不能改動 { Test.parseLog(log); } } } //parseLog方法內部的代碼不能改動 public static void parseLog(String log){ System.out.println(log+":"+(System.currentTimeMillis()/1000)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
使用阻塞隊列可以解決此問題
public class Test { public static void main(String[] args){ //使用阻塞隊列可以解決此問題,() 中的參數可以為 1 final BlockingQueue queue = new ArrayBlockingQueue<>(16); for(int i = 0;i<4;i++){ new Thread(new Runnable(){ @Override public void run() { while(true){ try { String log = (String)queue.take(); parseLog(log); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } System.out.println("begin:"+(System.currentTimeMillis()/1000)); /*模擬處理16行日志,下面的代碼產生了16個日志對象,當前代碼需要運行16秒才能打印完這些日志。 修改程序代碼,開四個線程讓這16個對象在4秒鍾打完。 */ for(int i=0;i<16;i++){ //這行代碼不能改動 final String log = ""+(i+1);//這行代碼不能改動 { try { queue.put(log); } catch (InterruptedException e) { e.printStackTrace(); } // Test.parseLog(log); } } } //parseLog方法內部的代碼不能改動 public static void parseLog(String log){ System.out.println(log+":"+(System.currentTimeMillis()/1000)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
第二題:現成程序中的Test類中的代碼在不斷地產生數據,然后交給TestDo.doSome()方法去處理,就好像生產者在不斷地產生數據,消費者在不斷消費數據。請將程序改造成有10個線程來消費生成者產生的數據,這些消費者都調用TestDo.doSome()方法去進行處理,故每個消費者都需要一秒才能處理完,程序應保證這些消費者線程依次有序地消費數據,只有上一個消費者消費完后,下一個消費者才能消費數據,下一個消費者是誰都可以,但要保證這些消費者線程拿到的數據是有順序的。原始代碼如下:
package queue; public class Test { 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); } } } //不能改動此TestDo類 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; } }
使用 SychronousQueue(同步隊列)來解決問題
public class Test { public static void main(String[] args) { //同步隊列,檢測到有線程等待取數據時,才會往queue中放入數據 final SynchronousQueue queue = new SynchronousQueue(); //這里使用個數為1信號燈,保證一次只有一個線程取數據,目的是為了同步,也可以使用Lock final Semaphore semaphore = new Semaphore(1); for(int i = 0;i<10;i++){ new Thread(new Runnable(){ public void run(){ try { semaphore.acquire(); String input = (String)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) { // TODO Auto-generated catch block e.printStackTrace(); } } } } //不能改動此TestDo類 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; } }
第三題:現有程序同時啟動了4個線程去調用TestDo.doSome(key, value)方法,由於TestDo.doSome(key, value)方法內的代碼是先暫停1秒,然后再輸出以秒為單位的當前時間值,所以,會打印出4個相同的時間值,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199615
請修改代碼,如果有幾個線程調用TestDo.doSome(key, value)方法時,傳遞進去的key相等(equals比較為true),則這幾個線程應互斥排隊輸出結果,即當有兩個線程的key都是"1"時,它們中的一個要比另外其他線程晚1秒輸出結果,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199616
總之,當每個線程中指定的key相等時,這些相等key的線程應每隔一秒依次輸出時間值(要用互斥),如果key不同,則並行執行(相互之間不互斥)。原始代碼如下:
package syn; //不能改動此Test類 public class Test extends Thread{ private TestDo testDo; private String key; private String value; public Test(String key,String key2,String value){ this.testDo = TestDo.getInstance(); /*常量"1"和"1"是同一個對象,下面這行代碼就是要用"1"+""的方式產生新的對象, 以實現內容沒有改變,仍然相等(都還為"1"),但對象卻不再是同一個的效果*/ this.key = key+key2; this.value = value; } public static void main(String[] args) throws InterruptedException{ Test a = new Test("1","","1"); Test b = new Test("1","","2"); Test c = new Test("3","","3"); Test d = new Test("4","","4"); System.out.println("begin:"+(System.currentTimeMillis()/1000)); a.start(); b.start(); c.start(); d.start(); } public void run(){ testDo.doSome(key, value); } } class TestDo { private TestDo() {} private static TestDo _instance = new TestDo(); public static TestDo getInstance() { return _instance; } public void doSome(Object key, String value) { // 以大括號內的是需要局部同步的代碼,不能改動! { try { Thread.sleep(1000); System.out.println(key+":"+value + ":" + (System.currentTimeMillis() / 1000)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
主要是線程的互斥
//不能改動此Test類 public class Test extends Thread { private TestDo testDo; private String key; private String value; public Test(String key, String key2, String value) { this.testDo = TestDo.getInstance(); /* * 常量"1"和"1"是同一個對象,下面這行代碼就是要用"1"+""的方式產生新的對象, * 以實現內容沒有改變,仍然相等(都還為"1"),但對象卻不再是同一個的效果 */ /* * a = "1"+""; b = "1"+""; a, b是同一個對象編譯器會自動優化常量,但是key1+key2編譯器無法自動進行優化 * 所以this.key不是同一個對象 */ this.key = key + key2; this.value = value; } public static void main(String[] args) throws InterruptedException { Test a = new Test("1", "", "1"); Test b = new Test("1", "", "2"); Test c = new Test("3", "", "3"); Test d = new Test("4", "", "4"); System.out.println("begin:" + (System.currentTimeMillis() / 1000)); a.start(); b.start(); c.start(); d.start(); } public void run() { testDo.doSome(key, value); } } class TestDo { private TestDo() { } private static TestDo _instance = new TestDo(); public static TestDo getInstance() { return _instance; } // 因為對集合記性迭代的時候不能修改集合,所以這里使用 CopyOnWriteArrayList private CopyOnWriteArrayList list = new CopyOnWriteArrayList(); public void doSome(Object key, String value) { Object o = key; if (!list.contains(o)) { list.add(o); } else { for (Iterator iter = list.iterator(); iter.hasNext();) { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } if (iter.next().equals(key)) { o = iter.next(); } } } synchronized (o) // 以大括號內的是需要局部同步的代碼,不能改動! { try { Thread.sleep(1000); System.out.println(key + ":" + value + ":" + (System.currentTimeMillis() / 1000)); } catch (InterruptedException e) { e.printStackTrace(); } } } }