zoukankan      html  css  js  c++  java
  • Java面试题1_多线程并发


    1.

    第一题:现有的程序代码模拟产生了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 Interview1 {
        
        public static void main(String[] args){
            
            System.out.println("begin:"+(System.currentTimeMillis()/1000));
            /*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
            修改程序代码,开四个线程让这16个对象在4秒钟打完。
            */
            ExecutorService threadPool=Executors.newFixedThreadPool(4);
            
                for(int i=0;i<16;i++){  //这行代码不能改动
                    final String log = ""+(i+1);//这行代码不能改动
                    {    
                          threadPool.execute(new Runnable(){//提交16个任务给线程池
                             public void run(){
                              Interview1.parseLog(log);
                             }
                          });
                             
                    }
                  
                
                }
               threadPool.shutdown();
            
        }
        
        //parseLog方法内部的代码不能改动
        public static void parseLog(String log){
            //为了输出明确添加线程名称
            System.out.println(Thread.currentThread().getName()+"..."+log+":"+(System.currentTimeMillis()/1000));
            
          try {
                Thread.sleep(1000);//可以增加每个线程执行Interview1.parseLog(log)的概率
                                   //不加可能该线程很快执行完空闲,又去执行下一个任务,其它线程机会较少
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
        }
        
    }

    多线程并发面试题1

    解决方式二:采用阻塞队列:

    class Interview1_2 {
        
        public static void main(String[] args){
            
            System.out.println("begin:"+(System.currentTimeMillis()/1000));
            /*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
            修改程序代码,开四个线程让这16个对象在4秒钟打完。
            */
            final ArrayBlockingQueue<String> blockQueue=new ArrayBlockingQueue<String> (1);//可以是任意值,下面有循环控制最好1~16
            for(int i=0;i<4;++i)
                new Thread(new Runnable(){
    
                    @Override
                    public void run() {
                       while(true){
                               try {
                                Interview1_2.parseLog(blockQueue.take());
                            } catch (InterruptedException e) {
                                // TODO 自动生成的 catch 块
                                e.printStackTrace();
                            }
                       }
                    }
                    
                    
                }).start();
            
                for(int i=0;i<16;i++){  //这行代码不能改动
                    final String log = ""+(i+1);//这行代码不能改动
                    {    
                        
                         try {
                            blockQueue.put(log);
                        } catch (InterruptedException e) {
                            // TODO 自动生成的 catch 块
                            e.printStackTrace();
                        }    
                        //Interview1_2.parseLog(log);
                        
                             
                    }
                  
                
                }
        
            
        }
        
        //parseLog方法内部的代码不能改动
        public static void parseLog(String log){
            //为了输出明确添加线程名称
            System.out.println(Thread.currentThread().getName()+"..."+log+":"+(System.currentTimeMillis()/1000));
            
            try {
                Thread.sleep(1000);//采用阻塞队列,这里如果不让每个线程sleep下,每个线程可能多次take
                                   
            } catch (InterruptedException e) {
                e.printStackTrace();
            }    
        }
        
    }

    多线程并发面试题1

    2.    

    第二题:现成程序中的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;
            }
        }

    为了控制取出的顺序,依然可以采用阻塞队列,每个消费者都需要一秒才能处理完:采用同步

    package com.itheima.thread.current.interview;
    
    import java.util.concurrent.ArrayBlockingQueue;
    
        public class Interview2 {
            private static ArrayBlockingQueue<String>  blockQueue=
                                          new ArrayBlockingQueue<String> (10);
            public static void main(String[] args) {
                
                System.out.println("begin:"+(System.currentTimeMillis()/1000));
               for(int i=0;i<10;i++){  //这行不能改动
                    String input = i+"";  //这行不能改改动
                    try {
                        blockQueue.put(input);
                    } catch (InterruptedException e1) {
                        // TODO 自动生成的 catch 块
                        e1.printStackTrace();
                    }
                    
                    
                    System.out.println(blockQueue);//可以看到put过程,运行结果主线程,一直在打印,这是因为子线程中doSome的sleep导致
                     
                    
                    
                    new Thread(new Runnable(){
                           @Override
                           public void run(){
                                    String output=null;
                                   synchronized(Interview2.class){
                                        try {
                                            output = TestDo.doSome(blockQueue.take());
                                        } catch (InterruptedException e) {
                                            // TODO 自动生成的 catch 块
                                            e.printStackTrace();
                                        }
                                          System.out.println(Thread.currentThread().getName()+ ":" + output);
                                         
                                       }
                              }
                           }).start();
                 }
              }
        }
        
        //不能改动此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;
            }
        }
    /*
     也可以尝试使用SynchronousQueue:一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。
     */

    Interview2

    3.

         第三题:现有程序同时启动了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();
                    }
                }
            }
    
        }

    第三道题难就难在如何让满足条件的线程的key.equals(anotherKey)==true互斥,不满足的不互斥

    思想:将doSome中的代码放到同步代码块中,通过改变锁来控制线程间是否同步

    在解决这个问题前,先看本题涉及的其它问题:

    代码中:

    /*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象, 以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/

    this.key = key+key2;

    this.value = value;

    导致a和b的key不是同一个对象,如果没有这个直接是this.key=key;那么直接将doSome中的代码放到synchronized(key)中就可以完成需求.

    为什么不是同一个对象?看一个小例子:

    package com.itheima.thread.current.interview;
    
    public class StringTest {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO 自动生成的方法存根
            String str1="1"+"a";
            String str2="1"+"a";
            System.out.println(str1==str2);//true
            compare(str1,str2);
        }
        public static void compare(String str1,String str2){
            String k1=str1+str2;
            String k2=str1+str2;
            System.out.println(k1==k2);//false
        }
    
    }

    反编译:

    public com.itheima.thread.current.interview.StringTest();
    Code:
       0: aload_0       
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return        
    
    public static void main(java.lang.String[]);
    Code:
       0: ldc           #16                 // String 1a
       2: astore_1      
       3: ldc           #16                 // String 1a
       5: astore_2      
       6: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
       9: aload_1       
      10: aload_2       
      11: if_acmpne     18
      14: iconst_1      
      15: goto          19
      18: iconst_0      
      19: invokevirtual #24                 // Method java/io/PrintStream.println:(Z)V
      22: aload_1       
      23: aload_2       
      24: invokestatic  #30                 // Method compare:(Ljava/lang/String;Ljava/lang/String;)
    V
      27: return        
    
    public static void compare(java.lang.String, java.lang.String);
    Code:
       
       //采用StringBuilder完成+动作,然后StringBuilder.toString()会new一个新的String对象
       0: new           #43                 // class java/lang/StringBuilder
       3: dup           
       4: aload_0       
       5: invokestatic  #45                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)L
    java/lang/String;
       8: invokespecial #49                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/S
    tring;)V
      11: aload_1       
      12: invokevirtual #52                 // Method java/lang/StringBuilder.append:(Ljava/lang/Str
    ing;)Ljava/lang/StringBuilder;
      15: invokevirtual #56                 // Method java/lang/StringBuilder.toString:()Ljava/lang/
    String;
      18: astore_2      
      
      
      19: new           #43                 // class java/lang/StringBuilder
      22: dup           
      23: aload_0       
      24: invokestatic  #45                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)L
    java/lang/String;
      27: invokespecial #49                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/S
    tring;)V
      30: aload_1       
      31: invokevirtual #52                 // Method java/lang/StringBuilder.append:(Ljava/lang/Str
    ing;)Ljava/lang/StringBuilder;
      34: invokevirtual #56                 // Method java/lang/StringBuilder.toString:()Ljava/lang/
    String;
      37: astore_3      
      38: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
      41: aload_2       
      42: aload_3       
      43: if_acmpne     50
      46: iconst_1      
      47: goto          51
      50: iconst_0      
      51: invokevirtual #24                 // Method java/io/PrintStream.println:(Z)V
      54: return        
    }

    再来解决上面的问题:

    public class Interview3 extends Thread{
            
            private TestDo2 testDo;
            private String key;
            private String value;
            
            public Interview3(String key,String key2,String value){
                this.testDo = TestDo2.getInstance();
                /*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
                以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
                this.key = key+key2; 
                this.value = value;
            }
    
    
            public static void main(String[] args) throws InterruptedException{
                Interview3 a = new Interview3("1","","1");
                Interview3 b = new Interview3("1","","2");
                Interview3 c = new Interview3("3","","3");
                Interview3 d = new Interview3("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 TestDo2 {
    
            private TestDo2() {}
            private ArrayList al=new ArrayList ();//定义一个集合
             //private CopyOnWriteArrayList al=new CopyOnWriteArrayList();
            private static TestDo2 _instance = new TestDo2();
            
            public static TestDo2 getInstance() {
                return _instance;
            }
    
            public void doSome(Object key, String value) {
             
                Object obj=key;
               synchronized(TestDo2.class){//这里必须加同步,即使用一个同步的集合(CopyOnArrayList或Collections返回的等)
                                       //可以使用同步的集合,然后在add前使用sleep来测试,每个线程可能均执行不到else
                   if(!al.contains(key)){  //同步集合只能保证并发修改数据不会出错(add,remove...)其实也就是只在add上加了同步
                        al.add(key);       //如果不加同步,很可能所有线程都执行al.add(key);
                   
                   }
                    else
                      //obj=al.get(al.indexOf(key));//index内部使用equals进行查找
                    
                      //一般使用迭代器:
                       for(Iterator it=al.iterator();it.hasNext();){//如果不加同步,可能出现一个线程add,切换到另一个线程it.next
                           Object getObj=it.next();                 //并发修改异常,同步的集合可以避免这个异常,保证在add时,
                                                                //其它线程不能it.next
                               if(getObj.equals(obj))               
                                  obj=getObj;
                       }
                      
               }
                // 以大括号内的是需要局部同步的代码,不能改动!
                synchronized(obj)
                {
                    try {
                        Thread.sleep(1000);
                        System.out.println(key+":"+value + ":"
                                + (System.currentTimeMillis()/1000 ));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }

    interview3

  • 相关阅读:
    606. Construct String from Binary Tree
    696. Count Binary Substrings
    POJ 3255 Roadblocks (次短路)
    POJ 2823 Sliding Window (单调队列)
    POJ 1704 Georgia and Bob (博弈)
    UVa 1663 Purifying Machine (二分匹配)
    UVa 10801 Lift Hopping (Dijkstra)
    POJ 3281 Dining (网络流之最大流)
    UVa 11100 The Trip, 2007 (题意+贪心)
    UVaLive 4254 Processor (二分+优先队列)
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3243948.html
Copyright © 2011-2022 走看看