android 的Thread类

Thread类

位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,学习Thread类包括这些相关知识:线程的几种状态、上下文切换,Thread类中的方法的具体使用。
线程:比进程更小的执行单元,每个进程可能有多条线程,线程需要放在一个进程中才能执行,线程由程序负责管理,而进程则由系统进行调度!
多线程的理解:并行执行多个条指令,将CPU时间片按照调度算法分配给各个线程,实际上是分时执行的,只是这个切换的时间很短,用户感觉到"同时"而已!

线程的状态

线程从创建到最终的消亡,要经历若干个状态,一般来说包括以下几个状态:

  • 创建(new)
  • 就绪(runnable)
  • 运行(running)
  • 阻塞(blocked)、主动睡眠(time waiting)、等待唤醒(waiting)
  • 消亡(dead)
    当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,譬如程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。

当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

当由于突然中断或者子任务执行完毕,线程就会被消亡。
在这里插入图片描述

原文链接

创建线程的三种方式

  1. 通过继承Thread类本身
class MyThread extends Thread {
    @Override
    public void run() {
         . . .
    }
}
     
//启动线程    
MyThread myThread = new MyThread ();
new MyThread().start();

  1. 实现Runnalbe接口
    实现Runnalbe接口,重载Runnalbe接口中的run()方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
class runnable implements Runnable {
    @Override
	public void run() {
	     . . .
	}
}
     
//启动线程  
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();

  1. 使用匿名方法类:
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tvThreadResult.setText("线程执行结果");
    }
}).start();

原文链接

上下文切换

对于单核CPU来说(对于多核CPU,此处就理解为一个核),CUP在某一个时刻只能运行一个线程,当在运行一个线程的过程中去运行另外一个线程,这个就叫做线程上下文切换。

由于可能当前的线程并没有执行完,所以在切换时需要保存线程的运行状态,以便下次线程切换回来的时候能够以上次状态去继续运行,举个简单的列子,比如一个线程A正在读取某个文件的内容,读取到一半的时候,此时CPU需要切换线程去执行线程B,当再次切换回来执行A的时候,我们不希望线程A从头开始读取,因此需要记录线程A的运行状态,下次线程回复的时候,我们需要知道线程执行到第几条指令了,搜易需要记录程序计数器的值,另外比如说线程正在进行某个计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起时变量的值时多少,因此需要记录CPU寄存器的状态。所以一般来说,线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据。

对于线程的上下文切换其实就是存储和回复CPU状态的过程,他使得线程能从断点处恢复执行。

虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样会带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以在进行多线程编程时要注意这些因素。

Thread类中的方法

Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务。

下面是Thread类中常用的方法:

  1. start()
    start()用来启动一个线程,当调用start()方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

  2. run()
    run()方法是不需要用户来调用的,当通过start()方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run()方法,在run()方法中定义具体要执行的任务。

  3. sleep()
    sleep方法有两个重载版本:

  • sleep(long millis) //参数为毫秒
  • sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒

sleep方法相当于让当前线程睡眠,交出CPU,让CPU去执行其他的任务

当前线程调用sleep()方法进入阻塞状态后,在其睡眠期间,该线程不会获得执行机会,即是系统中没有其他可执行线程,因此sleep方法常用来暂停程序执行。

但是有一点需要注意,sleep()方法不会释放锁,也就是说如果当前线程持有某个对象的锁,调用sleep()方法,其他线程就无法访问这个对象。

interrupt()

interrupt()方法解释为中断线程,实际是为了对线程做一个中断标记,但是线程还是可能还是会执行,不立即,不强制,默认不终止。

interrput()方法是替换stop()方法,stop()方法已经弃用,为什么弃用呢?
是这样,线程是一点一点执行,任何时间都有可能发生线程切换,任何时间都可以调用stop()方法,这个线程就会立即停止,可以产生非常随机的中间状态,比如在某个时间切到别的线程再也切不回来了,比如正在改某一个对象时线程停止了,会造成不可预估的影响。

所以我们要使用interrupt()方法,让程序去判断在什么时候中断当前线程,这样就能保证代码的健壮性和程序的可控性。

既然interrupt()不能立即停止线程,那么怎么才能让线程按照我们的要求停止呢?
这里我要介绍俩个方法:

  • isInterrupted()
  • Thread.interrupted()

用法:

//用于判断当前线程是否为中断状态,不会重置状态
if(isInterrupted()){
     //做一些收尾工作
     return ;
}

//用于判断当前线程是否为中断状态,先调用isInterrupted(boolean ClearInterrupted)方法,然后重置状态,true变为false,false还是false
if(Thread.interrupted()){
     //做一些收尾工作
     return ;
}

而且interrupt()可以打断睡眠状态,立即抛出异常。

	  //判断是否中断线程
  if(Thread.interrupted()){  //检查当前的线程,
     //收尾工作
  }
  try {
    Thread.sleep(2000);
  } catch (InterruptedException e) {
     //收尾工作
  }

yield()

yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。即让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,当某个线程调用了yield()方法之后,只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

join()

join方法有三个重载版本:

  • join()
  • join(long millis) //参数为毫秒
  • join(long millis,int nanoseconds) //第一参数为毫秒,第二参数为纳秒
    假如在main线程中,调用thread.join()方法,则main()方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join()方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的事件。
    代码示例:
public class ThreadDemo {

    private int i = 0 ;

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo() ;

        System.out.println("进入线程"+Thread.currentThread().getName());


        MyThread thread1 = threadDemo.new MyThread() ;
        thread1.start();
        System.out.println("线程等待"+Thread.currentThread().getName());
        try {
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程继续执行"+Thread.currentThread().getName());


    }

    class MyThread extends Thread{

        @Override
        public void run() {
            synchronized (ThreadDemo.class){
                i ++ ;
                System.out.println("线程:" + Thread.currentThread().getName() + " i = " + i);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程:" + Thread.currentThread().getName() + "--睡醒 ");

            }
        }
    }

}

获取线程属性的几个方法

  • getId() 得到线程的ID
  • getName和setName 用来得到或者设置线程名称。
  • getPriority和setPriority 用来获取和设置线程优先级。
  • setDaemon和isDaemon 用来设置线程是否成为守护线程和判断线程是否是守护线程。
  • 守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程不依赖,举个简单的例子,如果在main()线程中创建一个守护线程,当main()方法执行结束之后,守护线程也会随之消亡。而用户线程不会,用户线程会一直运行直到运行完毕,在JVM中,像垃圾收集器线程就是守护线程。
  • currentThread() 用来获取当前的线程
    8.wait()、notify()、notifyAll()
    wait()、notify()、notifyAll()这三个方法不是Thread类中的方法,是Object本地的final方法,但是多线程中也是不可或缺的。

wait()、notify()、notifyAll()和synchronized是配合使用的。

wait()在synchronized中在对应monitor维护等待队列,会把当前的锁让开,其他线程也可以访问同一个synchronized里面的代码。

notify()会唤醒同一个monitor的wait(),让monitor去唤醒,notify()唤醒wait()不确定是哪一个,所以一般不适用notify()这个方法。

notifyAll() 是唤醒同一个moitor所有的wait(),被唤醒后,需要到monitor的执行队列中等待,等待拿锁,拿锁后从wait()位置继续执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/82838.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

消息中间件-kafka实战-第六章-kafka加线程池多线程消费

目录 参考架构图延时队列 参考 头条面试:当线上Kafka集群有大量消息积压时,如何利用多线程消费解决消费积压问题 架构图 延时队列

vulnhub靶机DarkHole_2

靶机下载地址:DarkHole: 2 ~ VulnHub 靶机发现 arp-scan -l 扫描端口 nmap --min-rate 10000 -p- 192.168.21.145 扫描服务 nmap -sV -sT -O -p22,80 192.168.21.145 漏洞扫描 nmap --scriptvuln -p22,80 192.168.21.145 这里有git源码泄露 git clone mirrors…

网络编程基础(1)

目录 网络编程解决是跨主机的进程间通讯 1、网络 2、互联网 3、ip地址 (1)ipv4: (2)ipV6:1 (3)IP地址的组成: (4)Linux查看IP地址:ifconfig 4、mac地址 5、ping Ip地址 6…

Vue2-TodoList案例(初级 后面会进行完善)

🥔:觉得累是因为在走上坡路 本案例是初级案例,在下面几节会进行完善——Vue.js TodoList案例 组件化编码流程(通用)整体思路1、分析结构2、拆html和css3、初始化列表4、实现添加列表功能5、实现勾选功能6、实现删除功能…

第三讲:ApplicationContext的实现

这里写目录标题 一、前文回顾二、基础代码准备三、基于XML的ClassPathXmlApplicationContext1. 创建spring-config.xml配置文件2. 指定配置文件的路径 四、基于注解的AnnotationConfigApplicationContext1. 新增一个配置类2.指定配置类信息 五、基于注解和ServletWebServer应用…

Endnote在线链接pubmed的时候报错12057:不能连接到吊销服务器,或者未能获得最终响应?

​嘎嘎嘎问题如下: 解决办法: 打开控制面板: ok,完了之后再去EndNote就不会出现此问题了。(有的可能需要重启电脑,重启EndNote才会生效)

Docker 网络之 ipvlan 和 macvlan

Docker ipvlan 和 macvlan 引言 本文讲解了Docker 网络模式中的 ipvlan 和 macvlan 的区别,目前自己在生产环境中使用的 ipvlan 模式非常问题.也解决了实际业务问题. IPvlan L2 mode example ipvlan 无需网卡混杂模式 , 运行如下命令后可以生成一个 vlan 子接口 , 会和主网卡…

Ajax介绍

1.与服务器进行数据交换:通过 Ajax 可以给服务器发送请求,并获取服务器响应的数据。 2.异步交互:可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分网页 的技术,如: 搜索联想、用户名是否可…

SpringMVC之异常处理

SpringMVC之异常处理 异常分为编译时异常和运行时异常,编译时异常我们trycatch捕获,捕获后自行处理,而运行时异常是不可预期的,就需要规范编码来避免,在SpringMVC中,不管是编译异常还是运行时异常&#xff…

【教程】华南理工大学校园网登录抓包和协议模拟

每次手动登录特别麻烦,而且时不时断一下,因此搞个脚本让它定时监测、断开重连比较方便。这里不讲这个脚本怎么写,只记录一下登录时的抓包内容。 蒜了,直接上解析吧,也不复杂,相信大家一目了然。 目录 抓包…

JavaScript中的变量声明方式有哪些?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 变量声明方式var 声明(ES5及以前)let 声明(ES6以后)const 声明(ES6以后) ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者…

Docker自学:利用FastAPI建立一个简单的web app

环境配置:下载Docker Desktop 文件一:main.py from typing import Unionfrom fastapi import FastAPIimport uvicornapp FastAPI()app.get("/") def read_root():return {"Hello": "World"}app.get("/items/{item…

vs2022配置opencv进行监控 c++

下载opencv文件 下载好的目录结构是 以上就是用到的文件和目录 在vs2022配置 最后:此处运行提示找不到 opencv_world480.dll 解决办法:直接从 复制到windows下

【ElasticSearch】一键安装ElasticSearch与Kibana以及解决遇到的问题

目录 一、安装ES 二、安装Kibana 三、遇到的问题 一、安装ES 按顺序复制即可 docker network create es-net # 创建网络 docker pull images:7.12.1 # 拉取镜像 mkdir -p /root/es/data # 创建数据卷 mkdir -p /root/es/plugins # 创建数据卷 chmod 777 /root/es/** # 设置权…

基于Java+SpringBoot+Vue的学校田径运动会管理系统【源码+论文+演示视频+包运行成功】

博主介绍:✌擅长Java、微信小程序、Python、Android等,专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟 Java项目精品实战案…

RT-Thread 的环形缓冲区 ---- 镜像指示位

可以看一下这篇我写的博客,了解一下大概: RingBuffer 环形缓冲区----镜像指示位_呵呵哒( ̄▽ ̄)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132340883?spm1001.2014.3001.5501 【回顾】缓冲区…

Linux:shell脚本:基础使用(5)《正则表达式-sed工具》

sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用,功能不同凡响。 处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用s…

微服务概述-7

Shiro 框架 Shiro 是一个用于 Java 应用程序的安全框架。它提供了身份验证、授权、加密和会话管理等功能,可以帮助开发人员构建安全可靠的应用程序。 Java 中针对权限管理常见的有 2 个著名的框架:spring security 和 shiro shiro 基本概念 credentia…

AI引擎助力,CamScanner智能高清滤镜开启扫描新纪元!

文章目录 ⭐ 写在前面⭐ 突破图像处理难点:扫描全能王的独特优势⭐ 耳听为虚,眼见为实⭐ 产品背后的主要核心:AI-Scan助力⭐ 深度学习助力智能文档处理的国际化进程⭐ 品味智能文档处理的轻松与精准 ⭐ 写在前面 在数字化快速发展的今天&…

AE-卡通人物解说动画视频的制作

目录 1.导入卡通人物图片和音频文件 2.新建合成 3.在卡通人物图片上添加效果和表达式 4.在音频文件上添加效果和表达式 5.将卡通人物中的 CC Split2 中分割1 表达式链接到滑块中 6.卡通人物根据音频文件自动匹配口型。 AE制作卡通人物解说视频,卡通人物口型根据…