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 {@Overridepublic void run() {. . .}
}//启动线程    
MyThread myThread = new MyThread ();
new MyThread().start();
  1. 实现Runnalbe接口
    实现Runnalbe接口,重载Runnalbe接口中的run()方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
class runnable implements Runnable {@Overridepublic void run() {. . .}
}//启动线程  
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
  1. 使用匿名方法类:
new Thread(new Runnable() {@Overridepublic 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{@Overridepublic 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()位置继续执行。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp编写微信小程序遇到的坑总结

1、阻止事件冒泡 使用uniapp开发微信小程序的时候,发现使用click.stop来阻止事件冒泡没有作用,点击了之后发现仍然会触发父组件或者祖先组件的事件。 在网上查阅,发现使用tap.stop才能阻止事件冒泡。 2、二维码生成 在网上找了很多&…

adb对安卓app进行抓包(ip连接设备)

adb对安卓app进行抓包(ip连接设备) 一,首先将安卓设备的开发者模式打开,提示允许adb调试 二,自己的笔记本要和安卓设备在同一个网段下(同连一个WiFi就可以了) 三,在笔记本上根据i…

JVM——类的生命周期

文章目录 类加载过程加载验证准备解析初始化 卸载 一个类的完整生命周期如下: 类加载过程 Class 文件需要加载到虚拟机中之后才能运行和使用,那么虚拟机是如何加载这些 Class 文件呢? 系统加载 Class 类型的文件主要三步:加载->连接->…

CentOS系统环境搭建(十五)——CentOS安装Kibana

centos系统环境搭建专栏🔗点击跳转 关于Elasticsearch的安装请看CentOS系统环境搭建(十二)——CentOS7安装Elasticsearch。 CentOS安装Kibana 文章目录 CentOS安装Kibana1.下载2.上传3.解压4.修改kibana配置文件5.授予es用户权限6.kibana 后台…

uniapp的UI框架组件库——uView

在写uniapp项目时候,官方所推荐的样式库并不能满足日常的需求,也不可能自己去写相应的样式,费时又费力,所以我们一般会去使用第三方的组件库UI,就像vue里我们所熟悉的elementUI组件库一样的道理,在uniapp中…

​ Spring Clould 配置中心 - Nacos

视频地址:微服务(SpringCloudRabbitMQDockerRedis搜索分布式) Nacos配置管理-Nacos实现配置管理(P24、P25) Nacos除了可以做注册中心,同样可以做配置管理来使用。 当微服务部署的实例越来越多&#xff0c…

18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案WORD

导读:原文《18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案WORD》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 目 录 第一章 项目概述 1.1项目…

私域新零售商业模式成功的八大要素

从事互联网行业多年以来,遇到客户问最多的一个问题,就是什么样的模式火呀?在设计一个商业模式时,不单单只是考虑资金和人脉等等资源的,其实还是需要遵循这八大原则,它包括:客户价值最大化原则、…

PyTorch学习笔记(十三)——现有网络模型的使用及修改

以分类模型的VGG为例 vgg16_false torchvision.models.vgg16(weightsFalse) vgg16_true torchvision.models.vgg16(weightsTrue) print(vgg16_true) vgg16_true.classifier.add_module("add_linear",nn.Linear(1000,10)) print(vgg16_true) vgg16_false.classifie…

Docker+Selenium Grid搭建自动化测试平台

安装docker yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager –add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce -y Create a Docker Network docker network create grid 下载镜像 hu…

laravel-admin之 解决上传图片不显示 $form->image(‘image‘); 及 $grid->column(‘image‘);

参考 https://blog.csdn.net/u013164285/article/details/106017464 $grid->column(‘image’)->image(‘http://wuyan.cn’, 100, 100); // //设置服务器和宽高 图片上传的域名 上传的图片不显示 在 这里设置了图片的上传路径 在这里设置 域名 就可以回显图片

【计算机视觉|生成对抗】带条件的对抗网络进行图像到图像的转换(pix2pix)

本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 标题:Image-to-Image Translation with Conditional Adversarial Networks 链接:Image-to-Image Translation with Conditional Adversarial Networks | IEEE Conference Publicati…

Android DataStore:安全存储和轻松管理数据

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、使用3.1 Preferences DataStore添加依赖数据读…

LVS负载均衡集群-NAT模式部署

集群 集群:将多台主机作为一个整体,然后对外提供相同的服务 集群使用场景:高并发的场景 集群的分类 1.负载均衡器集群 减少响应延迟,提高并发处理的能力 2,高可用集群 增强系统的稳定性可靠性&…

Java SPI加载机制

SPI加载机制 SPI(Service Provider Interface)是一种通过外界配置来加载具体代码内容的技术手段。SPI是JDK内置的一种服务提供发现机制,用于实现框架的扩展和组件替换。 在SPI中,框架提供一整套接口,使用者实现这些接…

学习红外成像仪开发注意要点

学习红外成像仪开发注意要点 三河凡科科技飞讯红外成像仪开发学习注意要点 红外成像仪是一种高级的光学设备,可用于探测、分析和显示红外辐射,它广泛应用于医学、军事、石油、矿产资源勘探等领域。红外成像仪的开发需要注意以下几个方面: 1…

(搜索) 剑指 Offer 12. 矩阵中的路径 ——【Leetcode每日一题】

❓剑指 Offer 12. 矩阵中的路径 难度:中等 给定一个 m * n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构…

Spring项目使用Redis限制用户登录失败的次数以及暂时锁定用户登录权限

文章目录 背景环境代码实现0. 项目结构图(供参考)1. 数据库中的表(供参考)2. 依赖(pom.xml)3. 配置文件(application.yml)4. 配置文件(application-dev.yml)5…

在ubuntu+cpolar+rabbitMQ环境下,实现mq服务端远程访问

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

【Java转Go】快速上手学习笔记(二)之基础篇一

目录 创建项目数据类型变量常量类型转换计数器键盘交互流程控制代码运算符 创建项目 上篇我们安装好了Go环境,和用IDEA安装Go插件来开发Go项目:【Java转Go】快速上手学习笔记(一)之环境安装篇 。 这篇我们开始正式学习Go语言。我…