【第九关】线程基础

个人学习笔记
参考文章:
https://www.jianshu.com/nb/6977014
https://juejin.im/entry/593109e72f301e005830cd76
https://blog.csdn.net/xiao__gui/article/details/8188833?utm_source=blogxgwz7
https://www.jianshu.com/p/f7d4819b7b24
https://my.oschina.net/nenusoul/blog/794634

线程 - 介绍

定义:

一个基本的CPU执行单元&程序执行流的最小单元
> 比进程更小的可独立运行的基本单位,可理解为:轻量级进程
> 组成:线程ID+程序计数器+寄存器集合+堆栈
> 线程自己不拥有系统资源,与其他线程共享进程所拥有的全部资源。

作用:

减少程序在并发执行时所付出的时空开销,提高操作系统的并发性能

状态说明:

拥有类似于进程的就绪,阻塞,运行 3种基本状态,具体如下图:
在这里插入图片描述


线程分类

线程主要分为:守护线程,非守护线程(用户线程)

守护线程

  • 定义:守护用户线程的线程,即在程序运行时为其他线程提供一种通用服务
  • 常见:如垃圾回收线程
  • 设置方式:
    //设置该线程为守护线程
    thread.setDaemon(true)

非守护线程

主要包括:主线程&子线程

a.主线程(UI线程(MainThread))

  • 定义:Android系统在程序启动时会自动启动一条主线程
  • 作用:处理四大组件与用户进行交互的事情(如UI,界面交互相关)
  • :因为用户随时会与界面发生交互,因此主线程任何时候都必须保持很高的相应速度,所以主线程不允许进行耗时操作,否则会出现ANR(应用程序无响应)

b.子线程(工作线程)

  • 定义:手动创建的线程
  • 作用:耗时的操作(网络请求,I/O操作等)

守护线程与非守护线程的区别

区别:虚拟机是否退出

  • 当所有用户线程结束时,因为没有守护的必要,所以守护线程也会终止,虚拟机也同样退出;反过来,只要任何用户线程还在运行,守护线程就不会终止,虚拟机就不会退出

线程优先级

表示

线程优先级分为10个级别,分别用Thread类常量表示

// 譬如:
Thread.MIN_PRIORITY // 优先级1
Thread.MAX_PRIORITY // 优先级10

设置

  • 通过方法setPriority(int grade)进行优先级设置
  • 默认线程优先级是5,即Thread.NORM_PRIORITY

多线程 - 介绍

定义:

多个线程同时进行,即多个任务同时进行

1.其实,计算机任何特定时候只能执行一个任务;
2.多线程只是一种错觉:只是因为JVM快速调度资源来轮换线程,使得线程不断轮流执行,所以看起来好像在同时执行多个任务而已;

作用

Android官方声明:在多线程编程时有两大原则:

  1. 不要阻塞UI线程(即主线程) :单线程会导致主线程阻塞,然后出现ANR错误:主线程被阻塞超过5s则会出现错误。
  2. 不要在UI线程之外更新UI组件

所以,我们需要多线程(1个主线程+x个工作线程)来解决上述两个问题:

  • 将耗时任务放在工作线程中进行

对应原则:不要阻塞UI线程(即主线程),即当我们有耗时任务,如果在UI线程中执行,那就会阻塞UI主线程,必须要抛到工作(子 )线程中去执行

  • 将更新UI组件放在主线程中进行

对应原则:不要在UI线程之外访问UI组件,即更新UI组件时,一定要在UI线程里执行,故需要在工作线程中执行的任务结果返回到UI线程中去更新组件

应用场景

  • 将耗时任务从主线程抛到工作线程中进行
  • 将更新UI组件任务从工作线程抛到主线程中进行

实现方式

Android多线程实现方式包括:
在这里插入图片描述


使用方式

基础使用

  • 继承Thread类
  • 实现Runnable接口
  • Handler
继承Thread类
1. 简介

在这里插入图片描述

2. 使用讲解
2.1 使用步骤

在这里插入图片描述

2.2 具体使用
// 步骤1:创建线程类 (继承自Thread类)
   class MyThread extends Thread{

// 步骤2:复写run(),内容 = 定义线程行为
    @Override
    public void run(){
    ... // 定义的线程行为
    }
}

// 步骤3:创建线程对象,即 实例化线程类
  MyThread mt=new MyThread(“线程名称”);

// 步骤4:通过 线程对象 控制线程的状态,如 运行、睡眠、挂起  / 停止
// 此处采用 start()开启线程
  mt.start();
2.3 简便使用:匿名类

很多情况下,开发者会选择一种更加方便的方法去创建线程:匿名类

// 步骤1:采用匿名类,直接 创建 线程类的实例
 new Thread("线程名称") {
                 // 步骤2:复写run(),内容 = 定义线程行为
                    @Override
                    public void run() {       
                  // 步骤3:通过 线程对象 控制线程的状态,如 运行、睡眠、挂起  / 停止   
                      }.start();
2.4.两者区别

在这里插入图片描述

  • :调用Thread时,有两种方式:run(),start();run方法只是调用了Thread实例的run()方法而已,它仍然运行在主线程上,而start()方法会开辟一个新的线程,在新的线程上调用run()方法,此时它运行在新的线程上。
3. 与实现“Runnable”接口对比
  • 在Java中,继承Thread类和实现Runnable接口是实现多线程最常用的两种方法
    在这里插入图片描述
实现Runnable接口
1. 简介

在这里插入图片描述

2. 使用讲解
2.1 使用步骤

在这里插入图片描述
特别注意

  • Java中真正能创建新线程的只有Thread类对象
  • 通过实现Runnable的方式,最终还是通过Thread类对象来创建线程

所以对于实现了Runnable接口的类,称为线程辅助类;Thread类才是真正的线程类

2.2 具体使用
// 步骤1:创建线程辅助类,实现Runnable接口
 class MyThread implements Runnable{
    ....
    @Override
// 步骤2:复写run(),定义线程行为
    public void run(){

    }
}

// 步骤3:创建线程辅助对象,即 实例化 线程辅助类
  MyThread mt=new MyThread();

// 步骤4:创建线程对象,即 实例化线程类;线程类 = Thread类;
// 创建时通过Thread类的构造函数传入线程辅助类对象
// 原因:Runnable接口并没有任何对线程的支持,我们必须创建线程类(Thread类)的实例,从Thread类的一个实例内部运行
  Thread td=new Thread(mt);

// 步骤5:通过 线程对象 控制线程的状态,如 运行、睡眠、挂起  / 停止
// 当调用start()方法时,线程对象会自动回调线程辅助类对象的run(),从而实现线程操作
  td.start();
//MyThread myThread = new MyThread();
//new Thread(myThread).start();
2.3 简便使用:匿名类

很多情况下,开发者会选择一种更加方便的方法去创建线程:匿名类

    // 步骤1:通过匿名类 直接 创建线程辅助对象,即 实例化 线程辅助类
    Runnable mt = new Runnable() {
                    // 步骤2:复写run(),定义线程行为
                    @Override
                    public void run() {
                    }
                };

                // 步骤3:创建线程对象,即 实例化线程类;线程类 = Thread类;
                Thread mt1 = new Thread(mt, "窗口1");
           
                // 步骤4:通过 线程对象 控制线程的状态,如 运行、睡眠、挂起  / 停止
                mt1.start();
2.4 两种区别

在这里插入图片描述

Handler
1. 简介

在这里插入图片描述

2. 使用讲解

请查看文章:Android Handler详解

复合使用

Android多线程实现的复合使用包括:

  • AsyncTask
  • HandlerThread
  • IntentService
    称为“复用”的主要原因是:这3中方式的本质原理都是Android多线程基础实现的组合实现,下面,我将详细讲解。
AsyncTask
HandlerThread
IntentService

高级使用

Android 多线程的高级使用主要是线程池(ThreadPool)

ThreadPool
  • 简介
    在这里插入图片描述

优点

  • 重用线程池中的线程,避免频繁地创建和销毁线程带来地性能消耗

  • 有效控制线程的最大并发数量,防止线程过大导致抢占资源造成系统阻塞

  • 可以对线程进行一定的管理

  • 具体使用&工作原理:Android多线程:线程池ThreadPool 全面解析

补充

  • corePoolSize:线程池中核心线程的数量,默认情况下,即使核心线程没有任务在执行它也存在的,我们固定一定数量的核心线程且它一直存活这样就避免了一般情况下CPU创建和销毁线程带来的开销。我们如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程就会有超时策略,这个时间由keepAliveTime来设定,即keepAliveTime时间内如果核心线程没有回应则该线程就会被终止。allowCoreThreadTimeOut默认为false,核心线程没有超时时间。
  • maximumPoolSize:线程池中的最大线程数,当任务数量超过最大线程数时其他任务可能就会被阻塞。最大线程数 = 核心线程+非核心线程。非核心线程只有当核心线程不够用且线程池有空余时才会被创建,执行完任务后非核心线程会被销毁。
  • keepAliveTime:在终止之前等待新任务的最长时间,当执行时间超过这个时间时,非核心线程就会被回收。当allowCoreThreadTimeOut设置为true时,此属性也作用在核心线程上。
  • unit:枚举时间单位,TimeUnit。
  • workQueue:线程池中的任务队列,我们提交给线程池的runnable会被存储在这个对象上。

线程池分配规则:

  • 当线程池中的核心线程数量未达到最大线程数时,启动一个核心线程去执行任务;
  • 如果线程池中的核心线程数量达到最大线程数时,那么任务会被插入到任务队列中排队等待执行
  • 如果在上一个步骤中任务队列已满但是线程池中线程数量未达到限定线程总数,那么启动一个非核心线程来处理任务
  • 如果上一步骤中线程数量达到了限定线程总量,那么线程池则拒绝执行该任务,且ThreadPoolExecutor会调用RejectedtionHandler的rejectedExecution方法来通知调用者。

一般用法

  • shutDown():关闭线程池,需要执行完已提交的任务
  • shutDownNow():关闭线程池,并尝试结束已提交的任务
  • allowCoreThreadTimeOut(boolen):允许核心线程闲置超时回收
  • execute():提交任务无返回值
  • submit():提交任务有返回值

对比

在这里插入图片描述


线程调度

调度方式

  • 当系统存在大量线程时,系统会通过时间片轮转 的方式调度线程,因此线程不可能做到绝对的并发
  • 处于就绪状态(Runnable)的线程都会进入到线程队列(MessageQueue)中等待CPU资源

同一时刻在线程队列中可能有很多个

  • 在采用时间片的系统中,每个线程都有机会获得CPU的资源以便进行自身的线程操作;当线程使用CPU资源的时间到后,即使当前线程没有完成自己的全部操作,JVM也会中断当前线程的执行,把CPU资源的使用权切换给下一个队列中等待的线程。

被中断的线程将等待CPU资源的下一次轮回,然后从中断处继续执行

调度优先级

Java虚拟机(JVM)中的线程调度器负责管理线程,并根据以下规则进行调度:

  • 根据线程优先级(高 - 低),将CPU资源分配给各线程
  • 具备相同优先级的线程以轮流的方式获取CPU资源

示例:存在A,B,C,D四个线程,其中:A和B的优先级高于C和D(A,B同级,C,D同级),那么JVM将先以轮流的方式调度A,B,直到A,B线程死亡,再以轮流的方式调度C,D


线程同步

  • 定义:当线程A使用同步方法A时,其他线程必须等到线程A使用完同步方法A后才能使用。为什么呢,因为该关键字的作用是保证同一时刻最多只有1个线程执行被Synchronized修饰的方法/代码。
  • 同步方法用关键字Synchronized进行修饰
public synchronized void Sb_Android(){
        }

线程联合

  • 定义:线程A在占有CPU资源期间,通过调用join()方法中断自身线程执行,然后运行联合它的线程B,直到线程B执行完毕后线程A再重新排队等待CPU资源,这个过程称为线程A联合线程B
  • 线程A联合线程B,即在线程A的执行操作里定义:
B.join();

进程 - 介绍

定义

是进程实体的运行过程&系统进行资源分配和调度的一个独立单位

作用

使多个程序可并发执行,以提高系统的资源利用率和吞吐量

进程状态说明

在这里插入图片描述
注意 :就绪,阻塞状态的区别
就绪状态:进程缺少的资源 = 处理机,只要获得处理机资源立即执行
等待状态:指进程缺少其他资源(除了处理机)/等待某一事件

状态转换

在这里插入图片描述


进程与线程的区别

在这里插入图片描述


单/多进程,单/多线程的区别

假设:进程 = 桌子,单线程 = 1个人吃饭

  • 单进程、单线程:一个人在一个桌子上吃饭
  • 单进程、多线程:多个人在同一个桌子上一起吃饭
  • 多进程、单线程:多个人每个人在自己的桌子上吃饭

其他

线程同步:Synchronized关键字

在这里插入图片描述
具体请看文章:Java:手把手教你全面学习神秘的Synchronized关键字
Java线程同步:synchronized锁住的是代码还是对象

线程变量:ThreadLocal

在这里插入图片描述
具体请看文章:Java多线程:带你了解神秘的线程变量 ThreadLocal

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值