前言:
此时我们对“java银行代码”大概比较注意,你们都需要了解一些“java银行代码”的相关资讯。那么小编也在网上网罗了一些有关“java银行代码””的相关文章,希望咱们能喜欢,朋友们一起来学习一下吧!介绍
周末在水群的时候,发现有个小伙伴遇到了一个线上问题
线程池中线程的状态只有一个为RUNNABLE,其他都为WAITING,问有可能是哪些原因造成的?
线程池有25个线程,只有一个线程卡在网络读取上面,状态为RUNNABLE,其他线程都为WAITING。
本来我想让这个小伙伴把代码发过来看看的,可他却说自己做的是银行的项目,连不上外网,只能用手机开视频对着电脑让我看个大概。我复原一下这个代码的场景,估计很多小伙伴一下就能发现问题了,因为我把多余的代码都省略了,只留了会造成问题的代码
public class BankDemo { public ExecutorService service = Executors.newFixedThreadPool(5); public static class Task implements Runnable { private CountDownLatch latch; public void setLatch(CountDownLatch latch) { this.latch = latch; } @Override public void run() { // 建立一个Socket连接发送数据 Socket socket = new Socket("127.0.0.1",10006); // ... // 执行最后调用如下方法 latch.countDown(); } } // 真实的代码这里的过程为,每次往线程池里面放一批任务,这一批任务执行完毕,再放下一批任务 // 即循环调用如下方法 public void runTask(List<Task> taskList) throws InterruptedException { CountDownLatch latch = new CountDownLatch(5); taskList.forEach(item -> { item.setLatch(latch); service.submit(item); }); latch.await(); }}123456789101112131415161718192021222324252627282930313233
提示一下WAITING状态的线程阻塞在LockSupport.park()方法上
写个小插曲,这个小伙伴一直和我强调这个代码已经在线上跑了一年了,一直没发生问题。怎么到自己这就发生问题了,所以他的解决方案是一直看自己修改了哪些部分,但是始终没看出来问题。
而我的思路就和他不一样了,因为有些bug只有在特定场景下才会出现,不要坚信之前的代码就没有问题,要从问题本身着手
Java线程状态
在发现问题的时候基础知识还是很重要的,回顾一下
简易的线程状态如下图
Java Thread线程内部有一个枚举内部类State,定义了Java语言线程状态的枚举值
NEW(初始化状态)RUNNABLE (可运行/运行状态)BLOCKED(阻塞状态)WAITING (无时限等待)TIMED_WAITING(有时限等待)TERMINATED(终止状态)
Java将操作系统层面的阻塞状态细分为BLOCK,WAITING,TIMED_WAITING三种状态
NEW:新建状态,线程被创建但未启动的状态。创建线程有三种方式
继承Thread类实现Runnable接口实现Callable接口
我们最常用的是通过实现接口这种方式,Runnable和Callable接口的区别如下
Runnable无法获取返回值,而Callable可以获取返回值Runnable无法抛出异常,而Callable可以抛出异常
RUNNABLE(就绪状态):调用start之后运行之前的状态
RUNNING(运行状态):线程正在运行
BLOCKED(阻塞状态):进入以下状态,有以下几种情况
BLOCK(同步阻塞):锁被其他线程占用,如等待进入synchronized方法或者代码块WAITING(主动阻塞):执行Object.wait(),Thread.join()等TIMED_WAITING(等待阻塞):执行Object.wait(long),Thread.sleep(long)等
DEAD(终止状态):线程执行完毕
最后将各种方法补充到线程状态图上
场景还原
造成线程WAITING,一般是调用了如下3种方法之一
Object.wait()Thread.join()LockSupport.park()
排查问题的过程如下
在明确了代码中没有调用Object.wait()和Thread.join()后,那基本就确定了是调用了java.util.concurrent包下面的工具类导致的线程阻塞,因为java.util.concurrent包下的工具类频繁使用了LockSupport.park()接着就可以确定是使用CountDownLatch造成的问题了,其他的线程已经结束了,只有一个线程在运行,此时其他线程就阻塞等待那这个RUNNABLE的线程做啥了,为啥一直没有结束?此时文章最开始的一张图指明了方向,这个线程阻塞在网络读取上了。既然卡在网络读取上,肯定就是没有设置连接的超时时间,或者读取的超时时间。一问,果然和我想的一样,没有设置
设置完后,他在本地跑了一下,刚开始还正常运行,后来就直接抛出异常了
SocketTimeoutException: connect timed out(连接服务端超时)
SocketException: Connection reset(服务端关闭了连接,但是客户端还在从连接中读取数据)
那为什么刚开始程序能正常跑?后面就开始报这种连接异常了呢?
服务端确实并发太大了服务端的网路请求用BIO实现的,一个请求创建一个线程,本身就支持不了高并发
至于是哪种原因?小伙伴找服务端的开发人员确认一下就知道了。不过我猜很大概率是第二种原因,因为我看他们客户端就是用BIO写的,网络请求居然不用Netty,还是你们任性!
期待我后续的Netty文章哈,这种事情坚决不能再发生。
标签: #java银行代码