1. 主页 > 小妙招

Android PC通用!正确关闭线程的5个避坑指南

哎,不知道你们有没有遇到过这种情况?明明点了关闭按钮,程序还在后台偷偷运行;手机用着用着突然卡成PPT...(新手如何快速涨粉的运营技巧里也常提到这种问题)其实啊,八成是线程没关干净!今天咱们就唠唠这个坑了无数小白的"关线程"技术活。

第一坑:把线程当一次性筷子用

很多人写线程就像这样:"new Thread().start()"然后就不管了。结果呢?程序退出后线程还在后台吃内存!
??正确姿势:??

  1. 给线程设置名字(比如"DownloadWorker"),出问题时好排查
  2. 用线程池代替裸线程(后面会具体说)
  3. 一定要在Activity/Fragment销毁时主动关闭

举个反面教材:

java复制
// 错误写法!会导致内存泄漏  
new Thread(() -> {
    while(true) {
        // 下载文件...
    }
}).start();

第二坑:以为interrupt()能立刻刹车

新手最爱写的代码:

java复制
thread.interrupt();
thread = null;

结果发现线程还在跑!为啥?因为interrupt()只是发个通知,不是强制关机键。??关键点:??要在代码里自己检查中断状态!

看个正确案例:

java复制
while (!Thread.currentThread().isInterrupted()) {
    // 干活中...
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        // 这行必须加!重新标记中断状态
        Thread.currentThread().interrupt();
        break;
    }
}

第三坑:忘记释放资源就像上厕所不冲水

见过最离谱的bug:有个兄弟开了10个数据库连接,关线程时没释放,第二天系统直接瘫痪!
??必做清单:??

  • 文件流用finally块关闭
  • 网络连接记得disconnect()
  • 静态变量及时置null(特别是Android的Context)

对比下两种写法:

java复制
// 错误写法:可能导致文件锁死
public void run() {
    FileOutputStream fos = new FileOutputStream("data.txt");
    // 写数据...
}

// 正确写法:  
public void run() {
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream("data.txt");
        // 写数据...
    } finally {
        if(fos != null) fos.close();
    }
}

第四坑:把线程池当永动机

很多小白以为用了线程池就万事大吉,结果程序关了池子还在!来看组吓人数据:

线程池状态内存占用存活时间
正确关闭0MB立即结束
未关闭128MB超过5分钟

??救命代码:??

java复制
ExecutorService pool = Executors.newFixedThreadPool(4);
// 干活代码...
// 关闭时要这样:
pool.shutdown();
if(!pool.awaitTermination(5, TimeUnit.SECONDS)) {
    pool.shutdownNow();
}

第五坑:分不清守护线程和用户线程

这个坑Android上最要命!比如你开个后台下载线程,结果切个应用回来发现下载停了...

??速记口诀:??

  • 守护线程(Daemon):主人死了我跟着死(适合做辅助工作)
  • 用户线程:活没干完绝不狗带(默认类型)

设置方法超简单:

java复制
Thread daemonThread = new Thread();
daemonThread.setDaemon(true);

个人观点:关线程这事儿吧,就像谈恋爱分手——得干干净净!我见过太多人用thread.stop()这种暴力分手方式,结果闹得系统崩溃收场。最稳妥的还是用标志位+interrupt组合拳,就像分手时好好说清楚,该还的东西(资源)都还干净。下次写线程时,记得多问自己一句:"这线程知道什么时候该退出了吗?"

本文由嘻道妙招独家原创,未经允许,严禁转载