你有没有遇到过程序卡住不动的情况?点一下按钮,界面就没反应了,等半天也没结果。这种情况很可能是线程阻塞在作怪。别慌,这问题其实挺常见的,搞清楚原因后处理起来也不难。
什么是线程阻塞
简单说,线程阻塞就是某个任务在执行过程中被“卡住”了,没法继续往下走。比如你在做饭,水烧开了得关火,但你一直在等水开,别的事都干不了——这就相当于主线程被阻塞了。
常见引起阻塞的原因
最常见的就是网络请求或文件读写这类耗时操作放在主线程里做了。比如下面这段代码:
Thread thread = new Thread(() -> {
try {
String result = fetchFromNetwork(); // 网络请求
updateUI(result);
} catch (Exception e) {
e.printStackTrace();
}
});
thread.start();
看起来用了新线程,但如果这个请求迟迟得不到响应,而你又没设置超时时间,那这个线程就一直挂着,资源也被占用着。
怎么避免和处理
最直接的办法是给耗时操作加上超时机制。比如用 HttpURLConnection 时可以这样设置:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000); // 连接超时5秒
conn.setReadTimeout(10000); // 读取超时10秒
另外,别把耗时任务丢到 UI 线程里去跑。Android 开发中用 AsyncTask 或者更现代的协程,Java 项目里可以用线程池来管理任务。
比如用线程池处理多个请求:
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> {
// 执行具体任务
doSomethingHeavy();
});
这样既能控制并发数量,又能复用线程,减少创建销毁带来的开销。
排查工具也很重要
程序卡了不知道哪出问题?可以用 jstack 查看 Java 进程的线程堆栈。运行命令 jstack <pid>,就能看到每个线程当前的状态。如果发现某个线程长时间处于 BLOCKED 或 WAITING 状态,就得重点看看它在等什么。
开发时也可以加些日志,标记关键步骤的开始和结束时间。比如:
long start = System.currentTimeMillis();
Log.d("Task", "Start loading data...");
// 执行操作
Log.d("Task", "Finished in " + (System.currentTimeMillis() - start) + "ms");
有了这些信息,定位阻塞点就容易多了。
遇到线程阻塞别急着重启程序,先想想是不是哪里忘了设超时,或者任务放错了线程。小调整往往能解决大问题。