展开说说:Android实现多线程几种方式

1、线程是什么

关于线程,Android开发经常遇到的一个和线程相关的异常报错:NetworkOnMainThreadException,因为网络请求不可以运行在主线程(又称UI线程)。和网络请求一样的还有I/O操作、数据库操作等耗时任务一样都只能在子线程运行,他们都被成为耗时操作。那么线程到底是什么?线程是操作系统进行调度的最小单元,我们写的每一行代码都是在线程内执行的。所有更新UI的操作包括view、dialog、toast等展示和更新都必须在主线程执行,但如果在主线程执行耗时操作就会导致其他UI更新无法及时别响应,影响用户体验甚至引起ANR异常用户可能会决定退出甚至卸载您的应用。,所以耗时操作都必须在子线程执行。

子线程可以执行耗时操作但也不是越多越好,因为只有线程数量不超过cpu核心数量才会同时执行,一定那超过也是需要通过时间轮转调度而非同时进行。线程的存在即便什么也不执行也是会占用一定的内存开销并且执行创建和销毁线程的操作也会有系统开销,因此会有线程池的概念。

当应用组件启动时,如果应用没有运行任何其他组件,Android 系统会使用单个执行线程为应用启动新的 Linux 进程。默认情况下,同一应用的所有组件都在同一进程和线程(称为主线程)中运行

Android 的单线程模型有以下两条规则:

请勿阻塞界面线程。

请勿不能从界面或主线程之外的任何线程更新界面。

为了帮助您遵循这些规则,Android 提供了多种从其他线程访问界面线程的方式。以下列出了几种有用的方法:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

不过,随着操作变得越来越复杂,为了便于维护可以考虑在工作器线程中使用 Handler 处理从界面线程传送的消息。

2、使用子线程

主线程我们知道是在ThreadActivity的main方法中启动的,下面总结以下怎样开启一个子线程,结合日志一起看:

先打印一下主线程的名称和id:

Log.e(TAG, "   当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());

日志打印:05-04 04:04:52.520 5942-5942/com.example.testdemo3 E/ThreadActivity:    当前线程=      name=main    id=1

第一种、通过Thread+Runable的匿名内部类实现:

Thread thread = new Thread(new Runnable() {@Overridepublic void run() {//耗时任务Log.e(TAG, " Thread-run  当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());}
});thread.setName("俺是第一");if (threadIsRuning){thread.interrupt();threadIsRuning = !threadIsRuning;// 停止一个线程}else {thread.start();threadIsRuning = !threadIsRuning;}

日志信息:05-04 04:04:56.661 5942-5981/com.example.testdemo3 E/ThreadActivity:  Thread-run  当前线程=      name=俺是第一    id=217

第二种 和第一种相同只是将Runnable单独拎出来,写一个类实现Runnable接口

// 定义Runnable的实现类
public static class MyRunnable implements Runnable{@Overridepublic void run() {try {Thread.sleep(2000);Log.e(TAG, "Runnable-run  当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());} catch (InterruptedException e) {e.printStackTrace();}}
}MyRunnable myRunnable = new MyRunnable();
//                myRunnable.run();//还是当前线程Thread thread02 = new Thread(myRunnable);thread02.setName("俺是第二");thread02.start();

日志信息:05-04 04:05:08.664 5942-5982/com.example.testdemo3 E/ThreadActivity: Runnable-run  当前线程=      name=俺是第二    id=218

第三种 继承Thread类重写run方法

// 定义Thread类的子类
public static class MyThread extends Thread{//重写Thread类中的run方法,设置线程任务@Overridepublic void run() {Log.e(TAG, "MyThread- Thread-run  当前线程= " + "     name="+Thread.currentThread().getName()+ "    id="+Thread.currentThread().getId());//run方法此处打印: 05-04 03:52:55.402 5466-5466/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=main    id=1//start方法时此处打印:05-04 04:15:42.852 5942-6057/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=Thread-219    id=219}
}
//启动线程MyThread myThread = new MyThread();myThread.start(); //重新启动线程
//                myThread.run();//还是在当前线程,不会重新启动线程

如果上面是执行myRunnable.run();//还是当前线程的日志信息:05-04 03:52:55.402 5466-5466/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=main    id=1
如果上面是执行thread02.start(); //05-04 04:15:42.852 5942-6057/com.example.testdemo3 E/ThreadActivity: MyThread- Thread-run  当前线程=      name=Thread-219    id=219

这里调用了启动线程、停止线程、set和get线程名称以及id等方法。

2、Thread和Runnable什么关系

Thread就是Runnable的一个实现类:

Runnable:

Thread:

两中方式我们该怎样选,个人认为如果想简单点就用Thread想灵活一些请考虑Runnable。使用Runnable开启线程也离不开Thread,如果觉得调用runnable的run方法也可以开启线程执行任务就有点跑偏了。

先看一下Thread类的start()方法和run()方法的不同就理解为什么说跑偏了。

Thread类的start()方法和run()方法的主要区别在于人家扮演的角色压根就不一样。以下是两者的主要区别:

首先达到的效果不同。run()方法是线程中实际运行的代码,他就是仅代表调用run方法而已;start()方法是启动一个新线程,它会创建一个新的线程并在新的线程中并行执行run方法的任务。因此在实际的多线程编程中,应该通过调用start()方法来启动线程。

启动次数不同。run()方法是一个类中的普通方法,可以多次调用,每次调用都会执行一次任务,而start()方法只能被调用一次,如果再次调用start()方法,会抛出IllegalThreadStateException异常。//todo 每次停止线程,再启动需要重新new一个线程,否则报错:    java.lang.IllegalThreadStateException    at java.lang.Thread.start(Thread.java:878)     at com.example.testdemo3.activity.ThreadActivity$2.onClick(ThreadActivity.java:62)

所以runnable的run方法和Thread类的run()方法一样,run()方法是线程中实际运行的代码,他就是仅代表调用run方法而已,我们还是需要通过Thread的start方法开启新线程执行任务。

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

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

相关文章

奈氏准则和香农定理

一、奈奎斯特和香农 哈里奈奎斯特(Harry Nyquist)(左) 克劳德艾尔伍德香农(Claude Elwood Shannon)(右) 我们应该在心里记住他们,记住所有为人类伟大事业做出贡献的人,因为他们我们的生活变得越来越精彩&…

使用xshell工具连接ubuntu的root账户被拒绝的解决方法

问题描述: 我在使用xshell工具远程连接Ubuntu虚拟机的过程中,如果连接的是的普通用户则xshell工具可以正常连接,但是当我向连接ubuntu系统的root用户,即便是密码输入正确但还是不能连接成功。不能连接成功的截图如下: …

从零开始学AI绘画,万字Stable Diffusion终极教程(二)

【第2期】关键词 欢迎来到SD的终极教程,这是我们的第二节课 这套课程分为六节课,会系统性的介绍sd的全部功能,让你打下坚实牢靠的基础 1.SD入门 2.关键词 3.Lora模型 4.图生图 5.controlnet 6.知识补充 在第一节课里面,我们…

PS 2018

软件安装 文件太大,分批上传了,后续下载下来文件目录是这样的, 三个文件夹.7z 分批上传,exe也压缩分批上传, 其中products文件夹太大,里面子目录继续压缩分批上传 都下好了,就exe执行安装就行…

如何使用提示测试为LLMs构建单元测试?

原文地址:how-to-build-unit-tests-for-llms-using-prompt-testing 确保您的人工智能交付:快速测试完美生成应用程序的基本指南 2024 年 4 月 26 日 如果你曾经编写过软件,你就会知道测试是开发过程中必不可少的一部分。特别是单元测试&#…

Git推送本地项目到gitee远程仓库

Git 是一个功能强大的分布式版本控制系统,它允许多人协作开发项目,同时有效管理代码的历史版本。开发者可以克隆一个公共仓库到本地,进行更改后将更新推送回服务器,或从服务器拉取他人更改,实现代码的同步和版本控制。…

BUUCTF:Web 解析(一)

前言 Buuctf Web 是一个在线安全挑战平台,旨在提高参与者对网络安全的理解和实践能力。本文将详细介绍 Buuctf Web 的特点、挑战和机遇,帮助读者更好地了解这一领域。 一、Buuctf Web 的特点 多样化的挑战场景:Buuctf Web 提供了多种挑战场…

金属表面粗糙度对信号的影响

在进行PCB的传输线设计时,如果希望仿真结果更加贴合于实际的效果,就需要考虑很多的附加因素,比如,真实的叠构参数、介电常数、损耗角正切值、蚀刻因子、金属表面粗糙度、玻纤效应等,在常规的信号仿真中,前三…

springboot整合mybatis配置多数据源(mysql/oracle)

目录 前言导入依赖坐标创建mysql/oracle数据源配置类MySQLDataSourceConfigOracleDataSourceConfig application.yml配置文件配置mysql/oracle数据源编写Mapper接口编写Book实体类编写测试类 前言 springboot整合mybatis配置多数据源,可以都是mysql数据源&#xff…

基于RK1126的小型化低功耗AI相机,支持人体特征识别、人脸特征识别、案例帽识别等

提供可定制的⼀套 AI相机软硬件开发平台, 硬件采⽤ RockchipRV1126处理器解决 ⽅案,可选择搭配 SonyIMX系列传感器,POE供电与数据传输,采⽤ 38板标准结构设计,快速按需定制外壳,⽀撑从开发到验证到批量⽣产…

C#知识|事件集中响应,多个按钮关联同一事件(实例练习)

哈喽,你好,我是雷工! 本节学习窗体Controls集合、控件事件的统一关联及如何优化重复代码。 01 事件集中响应 原理:就是相同的控件,可以关联同一个事件响应方法。 02 示例演示 2.1、示例功能 该示例实现窗体中选择…

基于php+mysql+html简单图书管理系统

博主介绍: 大家好,本人精通Java、Python、Php、C#、C、C编程语言,同时也熟练掌握微信小程序、Android等技术,能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验,能够为学生提供各类…

Springboot图片上传【本地+oss】

文章目录 1 前端组件页面2 本地上传3 上传到阿里云oss3.1申请开通账号&#xff0c;做好先导准备3.2 开始使用 1 前端组件页面 使用的VueElement组件 在线cdn引入&#xff1a; <script src"https://cdn.bootcdn.net/ajax/libs/vue/2.7.16/vue.js"></script&…

Typescript语法

常量声明 let用于声明变量&#xff0c;而const用于声明常量。两者的区别是变量在赋值后可以修改&#xff0c;而常量在赋值后便不能修改。 const b:number 200; 类型判断 如果一个变量或常量的声明包含了初始值&#xff0c;TS便可以根据初始值进行类型判断&#xff0c;此时…

有限元分析

TOC在这里插入代码片 **应力挤压时的横截面内部力 剪应力&#xff1a;应力的相反力 法向力&#xff1a;施加的压力 与应力垂直 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解…

STM32入门学习之DMA

1.直接存储访问DMA(Direct Memory Access)&#xff1a;DMA传输不需要CPU的参与&#xff0c;直接在内存和I/O设备间开辟了一条新的数据传输通道&#xff0c;不仅提高数据传输的速率&#xff0c;还因为不需要CPU的干预&#xff0c;从而提高了CPU的利用率。(注&#xff1a;文中的资…

小红书API接口封装 | 根据笔记链接采集笔记详情,含正文内容、发布时间、转评赞藏等

一、背景介绍 1.1 爬取目标 用python开发的爬虫采集软件&#xff0c;可自动按笔记链接抓取笔记的详情数据。 为什么有了源码还开发界面软件呢&#xff1f;方便不懂编程代码的小白用户使用&#xff0c;无需安装python&#xff0c;无需改代码&#xff0c;双击打开即用&#xf…

零基础学习数据库SQL语句之操作表中数据的DML语句

我们的数据库是根据页面原型和相关需求完成相关开发的 在表中添加数据 删除数据 修改数据 添加数据 页面模型 当点击保存的时候就能将表单数据提交到服务端 服务端将数据添加到数据库 我们要用insert语句 将数据添加到数据库中 代码演示 CREATE DATABASE Dduo; USE Dduo…

在2-3-4树上实现连接与分裂操作的算法与实现

在2-3-4树上实现连接与分裂操作的算法与实现 引言1. 维护2-3-4树结点的高度属性伪代码示例 2. 实现连接操作伪代码示例 3. 证明简单路径p的划分性质4. 实现分裂操作伪代码示例 C代码示例结论 引言 2-3-4树是一种平衡搜索树&#xff0c;它保证了树的高度被有效控制&#xff0c;…