Android 多线程编程初探

Android 中的多线程其实就是 JavaSE 中的多线程,只是为了方便使用,android 封装了一些类,如 AsyncTask、HandlerThread 等,在日常的开发过程中,我们往往需要去执行一些耗时的操作,例如发起网络请求,考虑到网速等其他外在的因素,服务器可能不会立刻响应我们的请求,如果不将这条操作放到子线程中去执行,就会造成主线程被阻塞,今天我们就从多线程的基础来一起探讨

一、线程的基本用法

 

对于 Andorid 多线程来说我们最新接触到的就是 Thread 和 Runnable 通常我们如下来启动一个新的线程

1)继承自 Thread 来创建子线程

 

定义一个线程只需要新建一个类继承自 Thread,然后重写父类的 run 方法即可

[java] view plain copy

  1. /**
  2. * 继承 Thread 创建子线程
  3. */
  4. class MyThread extends Thread {
  5. @Override
  6. public void run() {
  7. //处理具体的逻辑
  8. }
  9. }

那么如何启动这个线程呢?只需要 new 出 MyThread 的实例,然后调用它的 start() 方法,这样 run() 方法中的代码就会运行在子线程,如下:

[java] view plain copy

  1. new MyThread().start();

2)实现 Runnable 接口来创建子线程

继承方式耦合性高,更多时候我们会选择实现 Runnable 接口的方式来实现一个子线程,如下:

[java] view plain copy

  1. /**
  2. * 实现 Runnable 接口创建子线程
  3. */
  4. class MyThread implements Runnable {
  5. @Override
  6. public void run() {
  7. //处理具体的逻辑
  8. }
  9. }

如果使用这种写法,启动线程的方法也需要相应的改变,如下:

[java] view plain copy

  1. MyThread myThread = new MyThread();
  2. new Thread(myThread).start();

Thread 构造函数接受一个 Runnable 参数,我们 new 出的 MyThread 正是一个实现了 Runnable 接口的对象,所以可以直接将它传入到 Thread 的构造函数里,接着就和上面的一样了,调用 Thread 的 start() 方法,run() 方法中的代码就会在子线程当中运行了

3)直接使用匿名类来创建子线程

[java] view plain copy

  1. new Thread(new Runnable() {
  2. @Override
  3. public void run() {
  4. //处理具体的逻辑
  5. }
  6. }).start();

如果你不想再定义一个类去实现 Runnable 接口,也可以使用如上匿名类的方式来实现,这种实现方式也是我们最常用到的

以上这些就是我们来创建子线程时使用的不方式,基本和 Java 中一样

4)Thread 和 Runnable 的区别

 

实际上 Thread 也是一个 Runnable,它实现了 Runnable 接口,在Thread类中有一个 Runnable 类型的 target字段,代表要被执行在这个子线程中的任务,代码如下:

Thread 部分源码:

[java] view plain copy

  1. public class Thread implements Runnable {
  2. /* What will be run. */
  3. private Runnable target;
  4. /* The group of this thread */
  5. private ThreadGroup group;
  6. private String name;
  7. /*
  8. * The requested stack size for this thread, or 0 if the creator did
  9. * not specify a stack size.  It is up to the VM to do whatever it
  10. * likes with this number; some VMs will ignore it.
  11. */
  12. private long stackSize;
  13. /**
  14. * 初始化 Thread 并且将Thread 添加到 ThreadGroup 中
  15. *
  16. * @param g
  17. * @param target
  18. * @param name
  19. * @param stackSize
  20. */
  21. private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
  22. java.lang.Thread parent = currentThread();
  23. //group 参数为空,则获取当前线程的才线程组
  24. if (g == null) {
  25. g = parent.getThreadGroup();
  26. }
  27. this.group = g;
  28. this.name = name;
  29. //设置 target
  30. this.target = target;
  31. /* Stash the specified stack size in case the VM cares */
  32. this.stackSize = stackSize;
  33. }
  34. public Thread() {
  35. init(null, null, null, 0);
  36. }
  37. public Thread(Runnable target) {
  38. init(null, target, null, 0);
  39. }
  40. public Thread(ThreadGroup group, Runnable target) {
  41. init(group, target, null, 0);
  42. }
  43. public Thread(Runnable target, String name) {
  44. init(null, target, name, 0);
  45. }
  46. public Thread(ThreadGroup group, Runnable target, String name) {
  47. init(group, target, name, 0);
  48. }
  49. public Thread(ThreadGroup group, Runnable target, String name,
  50. long stackSize) {
  51. init(group, target, name, stackSize);
  52. }
  53. /**
  54. * 启动一个新的线程,如果target 不为空则执行 target 的 run 函数
  55. * 否者执行当前对象的run()方法
  56. */
  57. public synchronized void start() {
  58. //调用 nativeCreate 启动新线程
  59. nativeCreate(this, stackSize, daemon);
  60. started = true;
  61. }
  62. @Override
  63. public void run() {
  64. if (target != null) {
  65. target.run();
  66. }
  67. }
  68. }

上面是 Thread 的部分源码,我们看到其实 Thread 也实现了 Runnable 接口,最终被线程执行的是 Runnable,而非 Thread,Thread 只是对 Runnable 的包装,并且通过一些状态对 Thread 进行管理与调度,Runnable 定义了可执行的任务它只有一个无返回值的 run() 函数,如下:

[java] view plain copy

  1. public interface Runnable {
  2. /**
  3. * When an object implementing interface <code>Runnable</code> is used
  4. * to create a thread, starting the thread causes the object‘s
  5. * <code>run</code> method to be called in that separately executing
  6. * thread.
  7. * <p>
  8. * The general contract of the method <code>run</code> is that it may
  9. * take any action whatsoever.
  10. *
  11. * @see java.lang.Thread#run()
  12. */
  13. public abstract void run();
  14. }

当启动一个线程时,如果 Thread 的 Target 不为空时,则会在子线程中执行这个 target 的 run() 函数,否则虚拟机就会执行该线程自身的 run() 函数

二、线程的 wait、sleep、join、yield

Thread 的基本用法相对来说比较简单,通常就是复写 run() 函数,然后调用线程的 start() 方法启动线程,接下来我们来看看 wait、sleep、join、yield 的区别

1)wait

当一个线程执行到 wait() 方法时,它就进入到一个和该对象相关的等待池中,同时释放对象的机锁,使得其它线程可以访问,用户可以使用 notify、nitifyAll 或者指定睡眠时间来唤醒当前等待池中的线程,注意:wait()、notify()、notifyAll() 必须放在 Synchronized 块中,否则会抛出异常

2)sleep

该函数是 Thread 的静态函数,作用是使调用线程进入睡眠状态,因为 sleep() 是 Thread 类的静态方法,因此它不能改变对象锁,所以当在一个 Synchronized 块中调用 sleep() 方法时,线程虽然休眠了,但是对象的锁并没有被释放,其它线程无法访问这个对象,即使睡眠也持有对象的锁

3)join

等待目标线程执行完之后再继续执行

4)yield

线程礼让,目标线程由运行状态转换为就绪状态,也就是让出执行权限,让其它线程可以优先执行,但其他线程能否优先执行未知

三、线程池

 

当我们需要频繁的创建多个线程进行耗时操作时,每次都经过 new Thread 实现并不是一种好的方式,每次 new Thread 新建和销毁对象的性能较差,线程缺乏统一管理,可能无限制新建线程,相互之间竞争,可能占用过多系统资源导致死锁,并且缺乏定时执行,定期执行,线程中断等功能,Java 提供了 4 种线程池,它能够有效的管理,调度线程,避免过多的浪费系统资源,它的优点如下:

1)重用存在的线程,减少对象创建,销毁的开销

2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多的资源竞争,避免堵塞

3)提供定时执行、定期执行、单线程、并发数控制等功能

简单来说,线程池原理简单的解释就是会创建多个线程并进行管理,提交给线程的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度、管理使得多线程使用更加简单、高效,如下图:

线程池都实现了 ExecutorService 接口,改接口定义了线程池需要实现的接口,它的实现有  ThreadPoolExecutor 和 ScheduledThreadPoolExecutor,ThreadPoolExecutor 也就是我们运用最多的线程池实现,ScheduledThreadPoolExecutor 用于周期性执行任务,通常我们都不会直接通过 new 的形式来创建线程池,由于创建参数过程相对复杂一些,因此 JDK 给我们提供了一个 Executor 工厂类来简化这个过程

线程池的使用准则:

1)不要对那些同步等待的其他任务结果的任务排队,这可能会导致死锁,在死锁中所有所有线程都被一些任务所占用,这些任务依次等待排队任务的结果,而这些任务又无法执行,因为所有的线程处于忙碌状态

2)理解任务,要有效的调整线程池的大小,你需要理解正在排队的任务以及它们正在做什么

3)调整线程池的大小基本上就是避免两类错误,线程太少或线程太多,幸运的是对于大多数应用程序来说,太多和太少之间的余地相当宽

今天介绍的基本都是一些理论的东西,有的看起来可能没劲,大家就当了解一下吧,又到周五了,祝大家周末愉快

参考:郭神第一行代码,Android 进阶

时间: 2024-10-23 16:23:01

Android 多线程编程初探的相关文章

Android多线程编程(一)——多线程基础

什么是进程 一个进程是一个独立(self contained)的运行环境,它可以看作一个程序或者一个应用. 什么是线程 而线程是进程中执行的一个任务,Java运行环境是一个包含了不同累和程序的单一进程.线程可以被称为轻量级进程.线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源. Android线程 Android的线程,实际上和Java的多线程编程并没有什么本质上的不同.当我们需要执行一些耗时操作,比如说发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如

Android多线程编程之线程池学习篇(一)

Android多线程编程之线程池学习篇(一) 一.前言 Android应用开发中多线程编程应用比较广泛,而应用比较多的是ThreadPoolExecutor,AsyncTask,IntentService,HandlerThread,AsyncTaskLoader等,为了更详细的分析每一种实现方式,将单独成篇分析.后续篇章中可能涉及到线程池的知识,特此本篇分析为何使用线程池,如何使用线程池以及线程池的使用原理. 二.Thread Pool基础 进程代表一个运行中的程序,一个运行中的Android

android: 多线程编程基础

9.1   服务是什么 服务(Service)是 Android 中实现程序后台运行的解决方案,它非常适合用于去执行那 些不需要和用户交互而且还要求长期运行的任务.服务的运行不依赖于任何用户界面,即使 当程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行. 不过需要注意的是,服务并不是运行在一个独立的进程当中的,而是依赖于创建服务 时所在的应用程序进程.当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停 止运行. 另外,也不要被服务的后台概念所迷惑,实际上服务并不会

Android多线程编程方法及开启步骤

作者:华清远见讲师 实现线程的两种方式 使用继承的方法 class MyThread extends Thread{ @Override public void run(){ //处理具体的逻辑 } } 要启动这个线程,在主线程中新建一个该实例,调用其start()方法即可. 使用实现Runnable借口的方式 class MyThread implements Runnable{ @Override public void run(){ //处理具体的逻辑 } } 开启现成时,使用: MyTh

Linux多线程编程初探

Linux 线程介绍 进程与线程 典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情.有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务. 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位.在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器.程序本身只是指令.数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例. 线程是操作系统能够进行运算调度的最小单位

android ——多线程编程

1.定义一个线程只需要新建一个类继承自Thread.然后重写run()方法,并在里面编写耗时逻辑即可: class MyThread extends Thread{ public void run(){ //处理具体的逻辑 } } 启动的时候只需要new出MyThread的实例,然后调用它的start()方法,这样run()中写的代码就会在子线程中进行: new Mythread().start(); 2.更多时候使用实现Runnable接口的方式来定义一个线程 class MyThread i

Android——多线程编程练习题

随便选择两个城市作为预选旅游目标.实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市.分别用Runnable接口和Thread类实现. layout文件: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/a

Android多线程编程

一.线程的三种使用方法 1.新建一个类继承自Thread,然后重写父类的run()方法,在run()方法中编写耗时逻辑,如下所示: class MyThread()extends Thread{ @Override public void run(){ //耗时操作 } } 开启这个线程的方法:new出这个MyThread类的实例,调用strart()方法,new  MyThread().start() 2.新建一个类实现Runnable接口的方式定义一个线程,如下所示: class MyThr

Android开发实践:多线程编程小结

我们知道,Android系统为了提高程序的实时响应能力,不允许在UI线程中进行耗时的操作,否则会出现ANR异常,因此必须将耗时的任务放到非UI线程中执行.Android/Java提供了很多类来帮助大家完成异步操作,比如:Thread类,Timer类,AsyncTask类,HandlerThread类,以及Executor接口.这些类都分别在什么场合下使用呢? 本文简单地总结一下Android开发中常见的多线程类型和解决方案,并比较和分析了各个方案的区别,以便更好地理解和应用这些API接口. 1.