最后
俗话说,好学者临池学书,不过网络时代,对于大多数的我们来说,我倒是觉得学习意识的觉醒很重要,这是开始学习的转折点,比如看到对自己方向发展有用的信息,先收藏一波是一波,比如如果你觉得我这篇文章ok,先点赞收藏一波。这样,等真的沉下心来学习,不至于被找资料分散了心神。慢慢来,先从点赞收藏做起,加油吧!
另外,给大家安排了一波学习面试资料:
以上就是本文的全部内容,希望对大家的面试有所帮助,祝大家早日升职加薪迎娶白富美走上人生巅峰!
//阻塞主线程,等待线程t1和线程t2执行完毕
t1.join();
t2.join();
//校对数据并返回结果
checkResult = checkData(hasNoOrders, hasNoStock);
//将结果信息保存到数据校对信息表中
saveCheckResult(checkResult);
//检测是否存在未对账订单
checkOrders = checkOrders();
}
至此,我们基本上能够解决问题了。但是,还有没有进一步优化的空间呢?我们进一步往下看。
通过上面对系统优化,基本能够达成我们的优化目标,但是上面的解决方案存在着不足的地方,那就是在while循环里每次都要新建两个线程分别执行getHasNoOrders()方法和getHasNoStock()方法,了解Java多线程的小伙伴们应该都知道,在Java中创建线程可是个非常耗时的操作。所以,最好是能够将创建出来的线程反复使用。这里,估计很多小伙伴都会想到使用线程池,没错,我们可以使用线程池进一步优化上面的代码。
不过在使用线程池进一步优化时,我们会遇到一个问题,就是主线程如何等待子线程中的结果数据呢?说直白点就是:主线程如何知道子线程中的getHasNoOrders()方法和getHasNoStock()方法执行完了? 由于在之前的代码中我们是在主线程中调用子线程的join()方法等待子线程执行完毕,获取到子线程执行的结果后,继续执行主线程的逻辑。但是如果使用了线程池的话,线程池中的线程根本不会退出,此时,我们无法使用线程的join()方法等待线程执行完毕。
所以,主线程如何知道子线程中的getHasNoOrders()方法和getHasNoStock()方法执行完了? 这个问题就成了关键的突破点。这里,我们使用线程池进一步优化的代码如下所示。
//检测是否存在未对账订单
checkOrders = checkOrders();
//创建线程池
Executor executor = Executors.newFixedThreadPool(2);
while(checkOrders != null){
executor.execute(()->{
//查询未校对的订单信息
hasNoOrders = getHasNoOrders();
});
executor.execute(()->{
//查询未校对的库存记录
hasNoStock = getHasNoStock();
});
/如何知道子线程中的getHasNoOrders()方法和getHasNoStock()方法执行完了成为关键/
//校对数据并返回结果
checkResult = checkData(hasNoOrders, hasNoStock);
//将结果信息保存到数据校对信息表中
saveCheckResult(checkResult);
//检测是否存在未对账订单
checkOrders = checkOrders();
}
那么,如何解决这个问题呢?我们继续往下看。
相信细心的小伙伴们能够看出,整个业务的场景就是:一个线程需要等待其他两个线程的逻辑执行完毕后再执行。在Java的并发类库中,为我们提供了一个能够在这种场景下使用的类库,那就是CountDownLatch类。如果对CountDownLatch类不了解的小伙伴可以参考《浅谈AQS中的CountDownLatch、Semaphore与CyclicBarrier》一文。
使用CountDownLatch类优化我们程序的具体做法就是:在程序的while()循环中首先创建一个CountDownLatch对象,计数器的值初始化为2。分别在hasNoOrders = getHasNoOrders();
代码和hasNoStock = getHasNoStock();
代码的后面调用latch.countDown()
方法使得计数器的值分别减1。在主线程中调用latch.await()
方法,等待计数器的值变为0,继续往下执行。这样,就能够完美解决我们遇到的问题了。优化后的代码如下所示。
//检测是否存在未对账订单
checkOrders = checkOrders();
//创建线程池
Executor executor = Executors.newFixedThreadPool(2);
while(checkOrders != null){
CountDownLatch latch = new CountDownLatch(2);
executor.execute(()->{
//查询未校对的订单信息
hasNoOrders = getHasNoOrders();
latch.countDown();
});
executor.execute(()->{
//查询未校对的库存记录
hasNoStock = getHasNoStock();
latch.countDown();
});
//等待子线程的逻辑执行完毕
latch.await();
//校对数据并返回结果
checkResult = checkData(hasNoOrders, hasNoStock);
//将结果信息保存到数据校对信息表中
saveCheckResult(checkResult);
//检测是否存在未对账订单
checkOrders = checkOrders();
}
至此,我们就完成了系统的优化工作。
这次系统性能的优化,主要是将单线程执行的数据校对业务,优化成使用多线程执行。在平时的工作过程中,我们需要认真思考,找到系统性能瓶颈所在,找出在逻辑上不相干,并且没有先后顺序的业务逻辑,将其放到不同的线程中执行,能够大大提供系统的性能。
这次,对于系统的优化,我们最终使用线程池来执行比较耗时的查询订单与查询库存记录的操作,并且在主线程中等待线程池中的线程逻辑执行完毕后再执行主线程的后续业务逻辑。这种场景,使用Java中提供的CountDownLatch类再合适不过了。这里,再强调一下:CountDownLatch主要的使用场景就是一个线程等待多个线程执行完毕后再执行。如下图所示。
这里,也进一步提醒了我们:如果想学好并发编程,熟练的掌握Java中提供的并发类库是我们必须要做到的。
最后,给大家一个思考题:其实,上面的代码不是最优的,你有更好的优化方法吗?欢迎各位小伙伴在文末留言讨论呀!
如果你想进大厂,想升职加薪,或者对自己现有的工作比较迷茫,都可以私信我交流,希望我的一些经历能够帮助到大家~~
推荐阅读:
最后
面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典
- Java核心知识整理
Java核心知识
- Spring全家桶(实战系列)
- 其他电子书资料
Step3:刷题
既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。
以下是我私藏的面试题库:
期准备过程中非常重要的一点。
以下是我私藏的面试题库:
[外链图片转存中…(img-rH2tZnuF-1715543963948)]