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

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

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

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

1. 进程与线程

什么是进程?

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

什么是线程?

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

为什么要使用多线程

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

  • 使用多线程可以减少程序的响应时间。
  • 与进程相比,线程的创建和切换开销更小,同时多线程在数据共享方面效率非常高。
  • 使用多线程能简化程序的结构,使程序便于理解和维护。

2.线程的状态

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

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

image.png

线程创建后,调用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();
    }
}

还可以改写成如下写法

目录
相关文章
|
5天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
65 2
|
5天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
21 1
|
28天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
26天前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
42 10
|
28天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
22天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
22天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
45 3
|
26天前
|
算法 调度 开发者
多线程编程核心:上下文切换深度解析
在多线程编程中,上下文切换是一个至关重要的概念,它直接影响到程序的性能和响应速度。本文将深入探讨上下文切换的含义、原因、影响以及如何优化,帮助你在工作和学习中更好地理解和应用多线程技术。
34 4
|
26天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
3天前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程