Android多线程编程——线程基础

版权声明:https://blog.csdn.net/petterp https://blog.csdn.net/petterp/article/details/88087258

目录

1. 进程与线程

什么是进程?

什么是线程?

为什么要使用多线程?

2.线程的状态

3.创建线程

4.中断

5.安全的终止线程


Android沿用了Java的线程模型,一个Android应用在创建的时候会开启一个线程,我们叫它主线程或者UI线程。

如果我们想要访问网络或者数据库等耗时操作,都会开启子线程去处理,从 Android3.0 开始,系统要求网络访问必须在子线程中进行,否则会抛出异常;也就是为了避免主线程被耗时操作阻塞从而产生 ANR。

1. 进程与线程

什么是进程?

进程是操作系统结构的基础,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的基本单位。进程可以看做是程序的实体,同时,他也是线程的容器。

什么是线程?

 线程是操作系统调度的最小单元,也叫轻量级进程。在一个进程中可以创建多个线程,这些线程都拥有各自的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量。

 为什么要使用多线程?

 在操作系统级别上来看主要有以下几个方面:

  • 使用多线程可以减少程序的响应时间。

  • 与进程相比,线程的创建和切换开销更小,同时多线程在数据共享方面效率非常高。

  • 使用多线程能简化程序的结构,使程序便于理解和维护。

 

2.线程的状态

Java的线程运行的声明周期中可能会处于6中不同的状态。

  • New 新创建状态。线程被创建,还没有调用Start方法,在线程运行之前还有一些基础工作要做。

  • Runnable 可运行状态。一旦调用start方法,线程就处于 Runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。

  • Blocked 阻塞状态。表示线程被锁阻塞,它暂时不活动。

  • Waiting 等待状态,线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器重新激活它。

  • Timed waiting 超时等待状态。和等待状态不同的是,它是可以在指定的时间自行返回的。

  • Terminated 终止状态。 表示当前线程已经执行完毕。导致线程终止有两种情况: 第一种就是run方法执行完毕正常退出;第二种就是因为没有一个捕获的异常而终止了 run方法,导致线程进入了终止状态。

 

 线程创建后,调用Thread 的 Start方法,开始进入运行状态,当线程执行 wait 方法后,线程进入等待状态,进入等待状态的线程需要其他线程通知才能返回运行状态。超时等待相当于在等待状态加上了时间限制,如果超过时间限制,则线程返回运行状态。当线程调用到同步方法时,如果线程没有获得所则进入阻塞状态,当阻塞状态的线程获取到锁是则重新回到运行状态。当线程执行完毕或者遇到以外异常终止时,都会进入终止状态。

3.创建线程

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

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

public class MyClass extends Thread{
    @Override
    public void run() {
        System.out.println("Petterp");
    }

    public static void main(String[] args) {
        MyClass myClass=new MyClass();
        myClass.start();
    }
}

   

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

public class MyClass extends Thread{
    @Override
    public void run() {
        System.out.println("Petterp");
    }

    public static void main(String[] args) {
        MyClass myClass=new MyClass();
        myClass.start();
    }
}

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

Callable接口是属于Expecutor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能。

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

  • Callable 中的call方法可以抛出异常,而Runnable的fun方法不能抛出异常。

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

class TestCallable implements Callable{
        public Object call() throws Exception {
            Thread.sleep(1000);
            return "Petterp";
        }
    }


public class MyClass {
    public static void main(String[] args) {
        TestCallable testCallable=new TestCallable();
        ExecutorService service= Executors.newCachedThreadPool();
        Future future=service.submit(testCallable);
        try {
            //等待线程结束,并返回结果
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

 

4.中断

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

while(!Thread.currentThread().isIntterrupted()){
    
}

还可以调用Thread.interrupted() 来对中断标识位进行复位。但是如果一个线程被阻塞,就无法检测中断状态。如果一个线程处于阻塞状态,线程在检查中断标识符是如果发现中断标识位为 true,则会在阻塞方法调用处抛出 InterruptedException 异常,并且在抛出异常前将线程的中断标识位复位,即重新设置为false,需要注意的是被中断的线程不一定会终止,中断线程是为了引起线程的注意,被中断的线程可以决定如何去响应中断,如果是比较重要的线程则不会理会中断,而大部分情况则是线程会将中断作为一个终止的请求。另外,不要在底层代码里捕获 InterruptedExcepetion 异常后不做处理:如下所示

class TestCallable extends Thread{
    @Override
    public  void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }

可以使用以下两种方式处理异常:

  1. 在catch 语句中,调用Thread.currentThread.interrupt() 来设置中断状态(因为抛出异常后中断标识符为复位),让外界通过判断 Thread.currentThread().isInterrupted()来决定是否终止线程还是继续下去。

class TestCallable extends Thread{
    @Override
    public  void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    interrupted();
                }
            }
    }

2. 更好的做法就是,不适用try来捕获这样的异常,让方法直接抛出,这样调用者可以捕获这个异常,如下

class TestCallable extends Thread {
    @Override
    public void run() {

    }
    void myTask() throws InterruptedException {
        sleep(5000);
    }
}

5.安全的终止线程

class TestCallable extends Thread {
    int i=0;
    @Override
    public void run() {
        System.out.println(i++);
    }
}



public class MyClass {
    public static void main(String[] args) throws InterruptedException {
        final TestCallable tes=new TestCallable();
        tes.start();
        TimeUnit.MILLISECONDS.sleep(10);
        tes.interrupt();
    }
}

还可以改写成如下写法

class TestCallable extends Thread {
    int i=0;
    private volatile  boolean on=true;
    @Override
    public void run() {
        while (on){
            System.out.println(i++);
        }
        System.out.println("stop");
    }
    public void cancel(){
        on=false;
    }
}



public class MyClass {
    public static void main(String[] args) throws InterruptedException {
        final TestCallable tes=new TestCallable();
        tes.start();
        TimeUnit.MILLISECONDS.sleep(100);
        tes.cancel();
    }
}

猜你喜欢

转载自blog.csdn.net/petterp/article/details/88087258
今日推荐