展开说说: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.知识补充 在第一节课里面,我们…

【EXCEL_VBA_实战经验】利用VBA批量解除工作表保护并调整页面设置

要使用VBA批量解除工作表保护并调整页面设置,可以按照以下步骤操作: 1. 打开Excel文件,按下AltF11键,打开VBA编辑器。 2. 点击菜单栏的插入,选择模块,在模块中输入以下代码: vba Sub Unprotec…

SAS SQL

SAS SQL 语法SQL CREATESQL SELECTSQL SELECT with WHERE ClauseSQL UPDATESQL DELETE 语法 proc sql;select columnsfrom tablewhere columnsgroup by columns ; quit;SQL查询在proc sql语句之后,加上quit语句,用于结束SQL查询 SQL CREATE data exam…

【Python进阶(七)】——Series数据结构

🍉CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一|统计学|干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项,参与研究经费10w、40w级横向 文…

牛客Xorto

Xorto 题目描述 给定一个长度为n的整数数组&#xff0c;问有多少对互不重叠的非空区间&#xff0c;使得两个区间内的数的异或和为0。 输入描述: 第一行一个数n表示数组长度&#xff1b; 第二行n个整数表示数组&#xff1b; 1<n<1000,0<数组元素<100000。 输出…

PS 2018

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

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

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

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

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

String str = new String(“Hello, World!“);

在Java中&#xff0c;当你执行 String str new String("Hello, World!"); 这行代码时&#xff0c;实际上发生了几件事情&#xff1a; 字符串常量池中的字符串&#xff1a;首先&#xff0c;Java会检查字符串字面量 "Hello, World!" 是否已经在字符串常量池…

BUUCTF:Web 解析(一)

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

C++中的priority_queue模拟实现

目录 priority_queue模拟实现 priority_queue类定义 priority_queue构造函数 priority_queue类push()函数 priority_queue类pop()函数 priority_queue类size()函数 priority_queue类empty()函数 priority_queue类top()函数 仿函数与priority_queue类模拟实现 仿函数 …

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

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

数据结构---第十天(选择题)

121.当数据库损坏时&#xff0c;数据库管理员可通过&#xff08;&#xff09;恢复数据库 A.事务日志文件 B.主数据文件 C.DELETE语句 D.联机帮助文件 122.下列选项不属于表的操作权限是&#xff08;&#xff09; A.EXECUTE B.UPDATE C.SELECT D.DELETE 123.用于数据库恢…

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

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

修改conda源和虚拟环境路径

1、修改conda默认源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yes2、修改conda默认虚拟环境路…

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

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

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

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