Java多線程與並發庫高級應用-面試題


第一題:現有的程序代碼模擬產生了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();
            }
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM