Android 多线程编程 - 线程的基本使用

1.线程的状态

  • New:新创建状态。线程被创建,还没有调用start方法,在线程运行之前还有一些基础工作要做。
  • Runnable: 可运行状态。一旦调用start方法,线程就处于Runnable状态。一个可运行线程可能正在运行,也可能没有运行,这取决于操作系统给线程提供运行的时间。
  • Blocked: 阻塞状态。表示线程被锁阻塞,它暂时不活动。
  • Waiting: 等待状态。线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器重新激活它。
  • Timed waiting: 超时等待状态。和等待状态不同的是,它是可以在指定的时间自行返回的。
  • Terminated: 终止状态。表示线程已经执行完毕。导致线程终止有两种情况:第一种是run方法执行完毕正常退出;第二种就是因为一个没有捕获的异常而终止了run方法,导致线程进入终止状态。

2. 创建线程

线程的创建一般有以下3种方法,其中前两种为常用的方法。

2.1. 继承Thread类,重写run方法

Thread类本质上也是实现了Runnable接口的一个实例。需要注意的是调用start方法后并不是立即地执行多线程的代码,而是使该线程变为可运行状态,什么时候运行多线程代码是由操作系统决定的。

    public class TestThread extends Thread{
        @Override
        public void run() {
            super.run();
            System.out.println("Hello World!");
        }
        public static void main(String[] args) {
            Thread thread = new TestThread();
            thread.start();
        }
    }

2.2. 实现Runnable接口,并实现该接口的run方法

实现步骤如下:

  1. 自定义类并实现Runnable接口,实现run方法。
  2. 创建Thread子类的实例,用实现Runnable接口的对象作为参数实例化该Thread对象
  3. 调用Thread的start方法来启动该线程。
    public class TestRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
        public static void main(String[] args) {
            TestRunnable testRunnable = new TestRunnable();
            Thread thread = new Thread(testRunnable);
            thread.start();
        }
    }

2.3. 实现Callable接口,重写call方法

Callable接口实际是属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable接口更强大的功能,主要表现为以下3点:

  1. Callable可以在任务接受后提供一个返回值,Runnable无法提供这个功能。

  2. Callable中的call方法可以抛出异常,而Runnable的run方法不能抛出异常。

  3. 运行Callable可以拿到一个Future对象;Future对象表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下就可以使用Future来监视目标线程调用call方法的情况。但调用Future的get方法以获取结果时,当前线程就会被阻塞,直到call方法返回结果。

    public class TestCallable {

        public static class MyTestCallable implements Callable {
            @Override
            public String call() throws Exception {
                return "Hello World";
            }
        }

        public static void main(String[] args) {
            MyTestCallable myTestCallable = new MyTestCallable();
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            Future future = executorService.submit(myTestCallable);
            try {
                System.out.println(future.get());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

3. 终止线程

3.1. 理解中断

当线程的run方法执行完毕,或者在方法中出现没有捕获的异常时,线程将终止。在Java的早期版本中有一个stop方法,其它线程可以调用它终止线程,但是这个方法现在已经被弃用了。interrupt方法可以用来请求中断线程。当一个线程调用interrupt方法时,线程的中断标识位将被置位(中断标识位为true),线程会不时的检测这个中断标识位,以判断线程是否应该被中断。要想知道线程是否被置位,可以调用Thread.currentThread().isInterrupted(),如下所示:

while (!Thread.currentThread().isInterrupted()){
    //do something
}

还可以调用Thread.interrupted()来对中断标识位进行复位。但是如果一个线程被阻塞,就无法检测中断状态。如果一个线程处于阻塞状态,那么线程在检测标识位时若发现中断标识位为true,则会在阻塞方法调用处抛出InterruptedException异常,并且在抛出异常前将线程的中断标识位复位,即重新设置为false,被中断的线程可以决定如何去响应中断。如果是比较重要的线程,则不会理会中断;而大部分情况则是线程会将中断作为一个终止的请求。另外,不要在底层代码里捕获InterruptedException异常后不做处理。如果不知道抛出InterrptedException异常后如何处理,这里介绍两种合理的处理方式:

  1. 在catch子句中,调用Thread.currentThread.interrupt()来设置中断状态(因为抛出异常后中断标识位会复位),让外界通过判断Thread.currentThread().isInterrupted()来决定是终止线程还是继续下去。
void myTask(){
    ...
    try {
        sleep(50);
    }catch (InterruptedException e){
        Thread.currentThread().interrupt();
    }
}

  1. 更好的做法就是,不使用try来捕获这样的异常,让方法直接抛出,这样调用者可以捕获这个异常,如下所示:
void myTask() throws InterruptedException{
    sleep(50);
}

3.2. 安全的终止线程

  1. 使用中断来终止线程
public class StopThread {
    public static void main(String[] args) {
        MoonRunner runnable = new MoonRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
    public static class MoonRunner implements Runnable{
        private long i;
        
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()){
                i++;
                System.out.println("i=" + i);
            } 
            System.out.println("stop");
        }
    }
}

  1. 采用boolean变量来控制是否终止线程
public class StopThread2 {
    public static void main(String[] args) throws InterruptedException {
        MoonRunner runnable = new MoonRunner();
        Thread thread = new Thread(runnable);
        thread.start();
        TimeUnit.MILLISECONDS.sleep(10);
        runnable.cancel();
    }
    public static class MoonRunner implements Runnable {
        private long i;
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on) {
                i++;
                System.out.println("i=" + i);
            }
            System.out.println("stop");
        }
        public void cancel(){
            on = false;
        }
    }
    
}

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值