当前位置:首页 > 公众号精选 > 架构师社区
[导读]多线程并发执行?线程之间通信?这是我偶尔听到我同事做面试官时问的一道题,感觉很有意思,发出来大家和大家讨论下。

详解一道京东面试题

多线程并发执行?线程之间通信?这是我偶尔听到我同事做面试官时问的一道题,感觉很有意思,发出来大家和大家讨论下

面试题目描述

现在呢,我们有三个接口,就叫他A,B,C吧,这三个接口都是查询某个人征信信息的,必须同时返回true,我们才认为这个人的征信合格,如果其中某一个返回false的话,就表明这个人的征信不合格,如果是你,你会怎么设计怎么写这个代码呢?

第一次思考

首先,一定是并发执行,假如说A接口执行3秒,B接口执行5秒,C接口执行8秒的话

  • 串行执行: 3+5+8 = 16秒
  • 并发执行: 8=8秒 (时间最久的那个接口执行的时间就是这三个接口的执行总时间)

熟悉的感觉,多线程执行任务,我在第二章文章实战!xhJaver竟然用线程池优化了。。。有提过怎么写,感兴趣的读者可以回去看一下,不过我在这里再写一下,话不多说来看下代码

并发代码

建议用PC端查看,所有代码都可直接复制运行,代码中重要的点都有详细注释

  1. 首先,我们先定义这三个接口
public class DoService { //设置A B C接口的返回值,b接口设置的是false private static Boolean flagA = true; private static Boolean flagB = false; private static Boolean flagC = true; public static boolean A(){ long start = System.currentTimeMillis(); try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            System.out.println("a被打断  耗时" + (System.currentTimeMillis() - start));
               e.printStackTrace();
        }
        System.out.println("a耗时  "+(System.currentTimeMillis() - start)); return flagA;
    } public static boolean B() { long start = System.currentTimeMillis(); try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            System.out.println("b被打断  耗时" + (System.currentTimeMillis() - start));
            e.printStackTrace();
        }
        System.out.println("b耗时  "+(System.currentTimeMillis() - start)); return flagB;
    } public static boolean C() { long start = System.currentTimeMillis(); try {
            Thread.sleep(8000L);
        } catch (InterruptedException e) {
            System.out.println("c被打断  耗时" + (System.currentTimeMillis() - start));
            e.printStackTrace();
        }
        System.out.println("c耗时  "+(System.currentTimeMillis() - start)); return flagC;
    }
}
  1. 其次 我们先创造一个Task 任务类
public class Task implements Callable<Boolean> { private String taskName; private Integer i; public Task(String taskName,int i){ this.taskName =taskName; this.i = i;
    } @Override public Boolean call() throws Exception { // 标记 返回值,代表这个接口是否执行成功 Boolean flag = false; //记录接口名字 String serviceName = null; //根据i的值来判断调用哪个接口 if (i==1){
            flag   =    DoService.A();
            serviceName="A";
        } if (i==2){
            flag   =    DoService.B();
            serviceName="B";
        } if (i==3){
            flag   =    DoService.C();
            serviceName="C";
        }
        System.out.println("当前线程是: "+Thread.currentThread().getName()+"正在处理的任务是: "+this.taskName+"调用的接口是: "+serviceName); return flag;
    }
}
  1. 最后,我们定义一个测试类
class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { //创建一个包含三个线程的线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); //事先准备好储存结果的list集合 List< Future> list = new ArrayList<>(); //开始计时 long start = System.currentTimeMillis(); for (int i=1;i<4;i++){
            Task task = new Task("任务"+i,i); //将每个任务提交到线程池中,并且得到这个线程的执行结果 Futureresult = executorService.submit(task);
            list.add(result);
        } //记得把线程池关闭 executorService.shutdown(); //定义一个变量 0 int count = 0;
        System.out.println("等待处理结果。。。"); for (int i=0;i//得到处理的结果 线程阻塞,如果线程没有处理完就一直阻塞 Boolean flag = result.get(); //如果这个接口返回true,那么count就++ if (flag){
                count++;
            }
        }
        System.out.println("线程池+结果处理时间:"+ (System.currentTimeMillis() - start)); //如果count数量为3,那么三个就都为true,代表这个人征信没问题 if (count==3){
            System.out.println("合格");
        }else {//否则,就是有问题 System.out.println("不合格");
        }
    }
}
  1. 我们看下输出结果
等待处理结果。。。
a耗时 3000 当前线程是: pool-1-thread-1正在处理的任务是: 任务1调用的接口是: A
b耗时 5000 当前线程是: pool-1-thread-2正在处理的任务是: 任务2调用的接口是: B
c耗时 8000 当前线程是: pool-1-thread-3正在处理的任务是: 任务3调用的接口是: C
线程池+结果处理时间:8008 不合格
  • 我们运行的时候会发现,它的输出结果的顺序如下 1 2 3 4 5 我们图中的2,3,4是再线程池内开了三个线程执行的,他们之间相隔一段时间才出现的,因为每个接口都有执行时间

程序运行后,“标记2”是3秒后出现,“标记三”是5秒后出现,“标记4”是8秒后出现

其实4和5相差时间很短,几乎是同时出现的,因为4执行完了就是主线程继续执行了

线程池+结果处理的时间一共是8秒,而每个接口分别执行的时间是3秒,5秒,8秒,达到了我们所说的,多线程处理多个接口,总共耗时时间是耗时最长的接口的时间

和京东面试官探讨

波哥说(我爱叫他波哥,东北人,说话则逗,幽默的人简直就是人间瑰宝,其实我也蛮有趣的,就是没人发现),你这程序不行啊,有个缺点,假如说,你这个A接口,耗时三秒,他返回了false,那么你另外两个线程也不用执行了,这个人的征信已经不合格了,你需要判断下,如果某一个线程执行的任务返回了false,那么就及时中断其他两个线程

灵光乍现

上一次的代码已经实现了多线程执行任务,可是这线程间通信怎么办呢?怎么才能根据一个线程的执行结果而打断其他线程呢?我想到了以下几点

  1. 共享变量 public static volatile boolean end = true;

  • 这个共享变量就代表是否结束三个线程的执行 如果为true的话,代表结束,false的话代表不结束线程执行
  • 计数器 public static AtomicInteger count =new AtomicInteger(0);

    • 每当每个线程执行完的话,如果返回true,计数器就+1,当计数器变为3的时候,就代表这个人征信没问题
  • 中断方法 interrupt()

    • 我们会单独开个线程一直循环检测这个变量,当检测到为true的时候,就会调用中断方法中断这三个线程
  • 阻塞线程 countDownLatch

    • 我们程序往下执行需要获取结果,获取不到这个结果的话,就要一直等着。我们可以用这个线程阻塞的工具,一开始给他设置数量为1,当满足继续向下执行的条件时,调用 countDownLatch.countDown();,在主线程那里 countDownLatch.await();一下这样当检测到数量为0的时候,主线程那里就继续往下执行了,话不多说,来看代码

    代码优化

    建议用PC端查看,所有代码都可直接复制运行,代码中重要的点都有详细注释

    1. 首先,还是创建接口
    public class DoService { private static Boolean flagA = true; private static Boolean flagB = false; private static Boolean flagC = true; public static boolean A(){ long start = System.currentTimeMillis(); try {
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                System.out.println("a被打断  耗时" + (System.currentTimeMillis() - start));
                   e.printStackTrace();
            }
            System.out.println("a耗时  "+(System.currentTimeMillis() - start)); return flagA;
        } public static boolean B() { long start = System.currentTimeMillis(); try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                System.out.println("b被打断  耗时" + (System.currentTimeMillis() - start));
                e.printStackTrace();
            }
            System.out.println("b耗时  "+(System.currentTimeMillis() - start)); return flagB;
        } public static boolean C() { long start = System.currentTimeMillis(); try {
                Thread.sleep(8000L);
            } catch (InterruptedException e) {
                System.out.println("c被打断  耗时" + (System.currentTimeMillis() - start));
                e.printStackTrace();
            }
            System.out.println("c耗时  "+(System.currentTimeMillis() - start)); return flagC;
        }
    }
    1. 创建任务
    public class Task implements Runnable { private String name ; public Task( String name){ this.name = name;
        } @Override public void run() { boolean flag = false;
            String serviceName = null; if(this.name.equals("A")){
                serviceName = "A";
                 flag = DoService.A();
            } if(this.name.equals("B")){
                serviceName = "B";
                flag = DoService.B();
            } if(this.name.equals("C")){
               serviceName = "C";
               flag = DoService.C();
           } //如果有一个为false if (!flag){ //就把共享标志位置为false Test.end = false;
            }else { //计数器加一,到三的话就是三个都为true Test.count.incrementAndGet();
           }
            System.out.println("当前线程是: "+Thread.currentThread().getName()+"正在处理的任务是: "+this.name+"调用的接口是: "+serviceName);
        }
    }
    1. 创建测试类
    class Test { //设置countDownLatch 里面计数为1, // 只调用一次countDownLatch.countDown就可以继续执行 countDownLatch.await(); //后面的代码了,接触阻塞 public static CountDownLatch countDownLatch = new CountDownLatch(1); //默认都为true,有一个线程为false了,那么就变为false public static volatile boolean end = true; //计数器,数字变为3的时候代表三个接口都返回true,线程安全的原子类 public static AtomicInteger count =new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); //创建三个任务,分被调用A B C 接口 Task taskA = new Task("A");
            Task taskB = new Task("B");
            Task taskC = new Task("C"); //创建三个线程 Thread tA = new Thread(taskA);
            Thread tB = new Thread(taskB);
            Thread tC = new Thread(taskC); //开启三个线程 tA.start();
            tB.start();
            tC.start(); //在开启一个线程,这个线程就是单独循环扫描这个共享变量的 new Thread(new Runnable() { @Override public void run() { //此线程一直循环判断这个结束变量,如果为false的话,就代表有一个接口返回false,跳出,重点其他线程 while (true){ if (!end ){ //当这个共享变量为false时i表示,其他线程可以中断了,所以就打断他们执行 tA.interrupt();
                            tB.interrupt();
                            tC.interrupt(); //如果某个线程被打断的话,就表明不合格 System.out.println("不合格"); //countDownLatch 计数器减一 countDownLatch.countDown(); break;
                        } if (Test.count.get()==3){
                            System.out.println("合格"); //countDownLatch 计数器减一 countDownLatch.countDown(); break;
                        }
                    }
                }
            }).start();
            System.out.println(Thread.currentThread().getName()+"主线程开始挂起"); //阻塞主线程继续执行,等待其他线程计算完结果在执行下去,countDownLatch中的计数为0时,就可以继续执行下去 countDownLatch.await();
            System.out.println(Thread.currentThread().getName()+" 主线获得结果后继续执行"+(System.currentTimeMillis() - start));
        }
    
    }
    1. 我们看下输出结果
    main主线程开始挂起
    a耗时 3024 当前线程是: Thread-0正在处理的任务是: A调用的接口是: A
    b耗时 5000 当前线程是: Thread-1正在处理的任务是: B调用的接口是: B
    c被打断  耗时5001 不合格
    java.lang.InterruptedException: sleep interrupted
     at java.lang.Thread.sleep(Native Method)
     at com.xhj.concurrent.executor_05._02.DoService.C(DoService.java:41)
     at com.xhj.concurrent.executor_05._02.Task.run(Task.java:30)
     at java.lang.Thread.run(Thread.java:748)
    c耗时 5003 当前线程是: Thread-2正在处理的任务是: C调用的接口是: C
    main 主线获得结果后继续执行5014 
    • 我们运行的时候会发现

    由图可见,我们首先就把主线程挂起,等待其他四个线程的处理结果,三个线程分别处理那三个接口,另外一个线程循环遍历那个共享变量,当检测到为false时,及时打断其他线程,这样的话,就解决了上面的那个问题。

    免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
    换一批
    延伸阅读

    业内消息,近日京东集团创始人、董事会主席刘强东以“采销东哥”AI 数字人开启直播首秀,同时亮相京东家电家居、京东超市采销直播间,不到1小时直播间观看量超2000万,并创造京东超市采销直播间开播以来观看人数的最高峰。

    关键字: 京东 AI 数字人 刘强东 直播

    最新消息,昨天京东官方发布了2023年四季度以及2023年全年财报。财报显示去四季度收入达到3061亿元,同比增长3.6%;全年收入10847亿元,同比增长3.7%;2023年净利润同比增长133%,美股大涨16%。

    关键字: 京东

    业内消息,上周在某知名求职平台上有员工爆料称, 中兴通讯正在进行规模型裁员的人员优化。近日也有小米员工透露,小米将在2024年2月29日进行大规模裁员,而赔偿标准则是N+1。此次裁员只是通知,没有任何协商的余地。与此同时...

    关键字: 中兴 小米 裁员 京东 涨薪

    2月3日消息,近日,京东创始人刘强东为江苏宿迁老家光明村1300多户村民送去了一份特别的年货——千余件羽绒服。

    关键字: 刘强东 京东

    2023年10月18日,中国在第三届“一带一路”国际合作高峰论坛期间发布《全球人工智能治理倡议》,围绕人工智能发展、安全、治理三方面系统阐述了人工智能治理中国方案。

    关键字: 人工智能 大模型 代码

    业内消息,近日有媒体报道称京东物流上个月底进行了一轮小规模的裁员,被裁员工对公司的赔偿方案不满不愿意签字,结果被公司单方面解除合同,人力资源部门让他们去走维权途径,部分人选择了仲裁,而公司方面则没有给出明确的裁员原因和标...

    关键字: 京东 裁员

    业内消息,近日京东一名运营人员就京东存在的一些问题在内网发表长文,比如复杂的促销机制、平台大促需要提前规划、给予商家更多生态支持以及低价心智等。随后京东创始人、董事局主席刘强东对该员工的内容作出评论,表示会走出低谷。

    关键字: 刘强东 京东

    近日,京东集团因为“承兴案”引发媒体和公众广泛关注,该事件起因是承兴集团的罗静造假,冒充京东工作人员并私自刻章,以京东、苏宁等应收账款来找金融机构(诺亚财富)贷款,最终骗走300亿并跑路暴雷,结果被抓,随后诺亚财富一纸诉...

    关键字: 京东

    我们看到这么多的安全问题,部分原因在于我们对待安全的方式:安全性通常被认为是事后考虑的问题,是在开发结束时才添加到设备上的东西。然而,复杂的系统,尤其是嵌入式系统,有一个很大的攻击面,这让攻击者有机可乘,能够在“盔甲”上...

    关键字: 代码 嵌入式系统 软件漏洞

    新富人群财务需求多元发展,投顾服务迎来新机遇 上海2023年9月20日 /美通社/ -- 2023年9月19日,上海交通大学上海高级金融学院(高金)与全球领先的金融服务机构嘉信理财(Charles Schwab)联合发...

    关键字: BSP ADVANCED INA 代码
    关闭
    关闭