当前位置: 首页 > news >正文

Android多线程学习:线程

一、概念

进程:系统资源分配的基本单位,进程之间相互独立,不能直接访问其他进程的地址空间。

线程:CPU调度的基本单位,线程之间共享所在进程的资源,包括共享内存,公有数据,全局变量等。

后台线程:后台线程又称为守护线程(Daemon Thread),JVM的垃圾回收线程就是典型的后台线程。

举例记忆:以下纯属本人瞎编,方便记忆

  • 进程就是一个鞋子工厂,鞋子由鞋带、鞋底、鞋帮三部分组成。线程就是工厂下的流水线,一条流水线做鞋带,一条流水线做鞋底,一条流水线做鞋帮,最后再把做好的组件组装起来变成一个鞋子。
  • 我们会把原材料直接提供给工厂,工厂统一接收而不是里面具体的某个流水线。所以工厂(进程)就是我们系统分配资源的最小单位。
  • 如果想做出鞋子,至少要开启一个流水线工作,这个流水线可以先做鞋带,在做鞋帮,在做鞋底,最后再组装成鞋子,如果没有流水线工作,一双鞋子也做不出来。所以流水线是系统可调度执行的基本单位。
  • 工厂包含多条流水线,即进程包含多个线程,而多条流水线上的工人又共享工厂里的食堂、厕所、宿舍,线程之间共享所在进程的资源。

二、线程三种实现

1、继承Thread类

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//3、创实例,并调用start()方法开启线程。new MyThread().start();new MyThread().start();}//1、定义一个类MyThread继承Thread,并重写run方法class MyThread extends Thread {@Overridepublic void run() {//2、将执行的代码写在run方法中。Log.d(TAG, "线程名字:" + Thread.currentThread().getName());}}
}

2、实现Runnable接口

public class MainActivity extends AppCompatActivity {public static final String TAG = MainActivity.class.getSimpleName();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//3、创建Thread对象, 传入MyRunnable的实例,并调用start()方法开启线程。Thread thread = new Thread(new MyRunnable());thread.start();Thread thread1 = new Thread(new MyRunnable());thread1.start();}// 1、定义一个类MyRunnable实现Runnable接口,并重写run方法。class MyRunnable implements Runnable {public void run() {//2、将执行的代码写在run方法中。Log.d(TAG, "线程名字:" + Thread.currentThread().getName());}}
}

3 、通过Callable和Future创建有返回值的多线程

public class MainActivity extends AppCompatActivity {private final String TAG = this.getClass().getSimpleName();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//3、创建线程池对象,调用submit()方法执行MyCallable任务,并返回Future对象ExecutorService pool = Executors.newSingleThreadExecutor();Future<Integer> f1 = pool.submit(new MyCallable());//4、调用Future对象的get()方法获取call()方法执行完后的值try {Log.d(TAG, "sum = " + f1.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}//5、关闭线程池pool.shutdown();}//1、自定义一个类MyCallable实现Callable接口,并重写call()方法public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//2、将要执行的代码写在call()方法中int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}}
}

三、线程的生命周期

1、线程的生命周期包括:新建New,就绪Runnable,运行Running,阻塞Blocked,和死亡Dead,5种状态。

新建:程序使用new关键字之后,该线程就处于新建状态,jvm为其分配内存,并初始化成员变量。

就绪:程序调用start() 方法之后,该线程就处于就绪状态,jvm为其创建方法调用栈和PC计数器。

运行:如果就绪状态的线程获得了CPU,那么程序就处于运行状态。

阻塞:指一个线程在执行过程中暂停,以等待某个条件的触发。

死亡:线程执行体执行结束,以及抛出一个未捕获的ExceptionError,或者直接调用stop()方法结束该线程。可以通过线程对象的isAlive()方法,来判断线程对象的状态。

2、生命周期转换

(1)运行到阻塞:

  • 线程调用sleep()方法,主动放弃所占有的CPU资源;
  • 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞;
  • 线程试图获得一个同步监视器,但是该同步监视器被其他线程所持有;
  • 线程等待某个通知notify()notify()通常与wait()配合使用;
  • 线程调用suspend(),挂起,该方法容易造成死锁,不建议使用。

(2)阻塞到就绪:

  • sleep()方法的线程经过了指定的sleep的时间;
  • 阻塞式IO方法返回值;
  • 成功获得了同步监视器;
  • 线程获得了其他线程发出的通知,被唤醒;
  • 挂起的线程调用了resume()方法恢复。

(3)状态转换图
线程生命周期.png

四、线程常用方法

1、setPriority(int newPriority) - 设置线程优先级

  • 设置线程的优先级来改变线程争抢到时间片的概率,优先级高的争抢到时间片的概率越大;
  • 优先级的取值范围:1~10,默认为5,数字越大,优先级越高;
  • 这个方法的调用必须在start之前,否则没有任何意义;
  • 使用方法getPriority(),获取当前线程优先级。

2、setDeamon(boolean b) - 设置后台线程

  • 如果所有的前台线程死亡,那么后台线程会自动死亡;
  • 默认情况下所有的线程都是前台线程;
  • 这个方法的调用必须在start之前。

3、sleep(long millis) - 设置线程休眠

  • 让当前线程暂停millis毫秒,并进入阻塞状态,睡眠状态的线程不会释放同步监视器,在此期间该线程不会获得执行的机会;
  • 使用sleep方法时需要捕捉InterruptedException或者抛出该异常。

4、interrupt() - 线程中断

  • 表示的并不是将线程结束,而是表示清除阻塞状态;
  • 中断线程操作实质上是修改了一下中断标示位为true
  • 如果线程处于阻塞状态,抛出异常InterruptedException
public class MainActivity extends AppCompatActivity {public static final String TAG = MainActivity.class.getSimpleName();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Thread thread = new Thread(new MyRunnable());thread.start();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}//打断休眠线程thread.interrupt();}class MyRunnable implements Runnable {public void run() {try {Log.i(TAG, "----开始休眠-----");Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();Log.i(TAG, "----线程中断-----");}Log.i(TAG, "----休眠后执行-----");}}
}

执行结果:报异常,提示中断,取消线程阻塞,执行休眠后操作。interrupt.PNG

public class MainActivity extends AppCompatActivity {public static final String TAG = MainActivity.class.getSimpleName();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Thread thread = new Thread(new MyRunnable());thread.start();thread.interrupt();}class MyRunnable implements Runnable {public void run() {for (int i = 0; i < 5; i ++){Log.i(TAG,"打印: " + i);if (Thread.interrupted()){Log.i(TAG, "----线程被中断了-----");}}Log.i(TAG, "----执行完了- interrupt state: " + Thread.interrupted());}}
}

执行结果:interrupted()只有主线程调用的时候才会是truefor循环结束后再次执行是falseinterrupt2.PNG

5、join() - 线程合并

  • 在执行原来的线程的过程中,如果遇到了合并线程,则优先执行合并进来的线程,当合并线程执行完毕之后,再接着执行原来的线程;
  • 调用join方法之前,一定要将线程start
  • join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()
public class MainActivity extends AppCompatActivity {public static final String TAG = MainActivity.class.getSimpleName();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Thread thread1 = new Thread(new JoinRunnable());thread1.start();for (int i = 0; i < 5; i++) {//主线程执行1的时候,把执行权让给了子线程if (i == 1) {try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}}Log.i(TAG, Thread.currentThread().getName() + "正在运行....");}}class JoinRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 3; i++) {try {Thread.sleep(1000);Log.i(TAG, Thread.currentThread().getName() + "正在运行....");} catch (InterruptedException e) {e.printStackTrace();}}}}
}

执行结果:
join3.PNG
6、yield() - 线程让步

  • 使得正在执行的线程暂停,但不会阻塞线程,释放自己拥有的CPU,线程进入就绪状态。
  • 只有优先级与当前线程相同,或者优先级比当前线程更高的线程才有可能获得执行机会,但是可能是当前线程又进入到“运行状态”继续运行。
public class MainActivity extends AppCompatActivity {public static final String TAG = "test";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Thread thread1 = new Thread(new MyRunnable());thread1.start();}class MyRunnable implements Runnable {@Overridepublic void run() {long begainTime = System.currentTimeMillis();int count = 0;for (int i = 0; i < 5000000; i++) {//结果2需要将下面注释放开//Thread.yield();count = count + (i + 1);}long endTime = System.currentTimeMillis();Log.i(TAG, "用时:" + (endTime - begainTime) + "ms");}}
}

运行结果1:
yield1.PNG

运行结果2,执行时放开Thread.yield()
yield3.PNG

  • yield()也不会释放锁标志,示例如下:
public class MainActivity extends AppCompatActivity {public static final String TAG = "test";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Thread thread1 = new Thread(new MyRunnable());Thread thread2 = new Thread(new MyRunnable());thread1.start();thread2.start();}static class MyRunnable implements Runnable {private static Object obj = new Object();@Overridepublic void run() {synchronized (obj) {for(int i = 0;i < 5;i++) {Log.i(TAG, Thread.currentThread().getName() + "正在执行i: " + i);if(i == 2) {Thread.currentThread().yield();}}}}}
}

运行结果,Thread-2获取锁以后,就算yield,也没释放锁:
yield4.PNG
7、stop() - 线程停止

  • 官方不建议使用该方法,因为stop不安全,stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了stop()方法,即使在同步块中,它也会马上stop了,这样就产生了不完整的脏数据。

参考文章:
对进程、线程、多线程、线程池的理解
带你通俗易懂的理解——线程、多线程与线程池
线程类的常见方法介绍
java多线程以及Android多线程

相关文章:

Android多线程学习:线程

一、概念 进程&#xff1a;系统资源分配的基本单位&#xff0c;进程之间相互独立&#xff0c;不能直接访问其他进程的地址空间。 线程&#xff1a;CPU调度的基本单位&#xff0c;线程之间共享所在进程的资源&#xff0c;包括共享内存&#xff0c;公有数据&#xff0c;全局变量…...

canvas 入门

canvas 入门 canvas是干什么的&#xff1f;canvas 绘制直线canvas画虚线canvas 绘制三角形canvas 绘制正方形canvas 绘制圆形、圆弧与椭圆canvas绘制文本canvas绘制图片 canvas是干什么的&#xff1f; <canvas> 是HTML5中的标签&#xff0c;它是一个容器&#xff0c;可以…...

建议收藏!混迹职场多年总结出的8大技巧!

1. 不要吃“哑巴”亏&#xff1a;不管在什么企业&#xff0c;一定要“会说话”&#xff0c;敢于表达自己&#xff0c;但是又兼顾身边人的感受&#xff0c;考虑好自己的言行将会带来的后果。良好的沟通技巧对于在职场中建立良好的人际关系和解决问题至关重要。学会倾听、表达和理…...

OpenCV4(C++)—— 视频和摄像头的加载、显示与保存

文章目录 一、加载与显示二、保存 一、加载与显示 视频或摄像头的加载是使用 cv::VideoCapture 类。&#xff08;这个类和 ifstream 类比较相似&#xff0c;视频或摄像头的加载和文本文件操作是大致相同。主要步骤&#xff1a;&#xff08;1&#xff09;加载&#xff08;打开&a…...

excel功能区(ribbonx)编程笔记6-box的使用

box元素用来在组里指定的控件周围放置一个可视的框,其主要目的是将控件作为一个单元组合在一起。 通常情况下,分配到组中的每个控件都被放置在先前的控件下面直到该列被填满,然后下一个控件被放置在其右侧列的顶行。然而,通过在框里面组合命令,可以将几个控件视作一个整体…...

oralce配置访问白名单的方法

目录 配置sqlnet.ora文件 重新加载使配置生效 注意事项 Oracle数据库安全性提升&#xff1a;IP白名单的配置方法 随着互联网的发展&#xff0c;数据库安全问题也越来越严重。Oracle是目前使用较为广泛的一款数据库管理系统&#xff0c;而IP白名单作为提升数据库安全性的有效…...

ToBeWritten之让响应团队参与并做好沟通

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…...

ffmpeg ts 关于av_seek_frame

1 ffmpeg命令行 一般对视频文件的裁剪 我们通过一行 ffmpeg命令行即可实现&#xff0c;比如 ffmpeg -ss 0.5 - t 3 - i a.mp4 vcodec copy b.mp4 其中 -ss 放置较前 开启精准seek定位 对于mp4而言 seek将从moov中相关索引表查找 0.5s时刻附近最近的关键帧 &#xff08;此描述…...

【C++】set map 的底层封装

在了解底层封装之前除了对set和map的使用情况要有一定了解&#xff0c;还需要先学习一下二叉搜索树&#xff0c;AVL树&#xff0c;红黑树这些数据结构。 【C】二叉搜索树 【C】AVL树 & 红黑树 RBTree.h enum Colour {RED,BLACK };template<class T> class RBTreeNo…...

JavaWeb整体介绍

JavaWeb整体介绍 什么是Java Web Web&#xff1a;全球广域网&#xff0c;也称为万维网&#xff08;www&#xff09;&#xff0c;能够通过浏览器访问的网站JavaWeb&#xff1a;是使用Java技术解决相关web互联网领域的技术栈&#xff08;就是用java开发网站&#xff09; 网页&a…...

一些常见分布-正态分布、对数正态分布、伽马分布、卡方分布、t分布、F分布等

目录 正态分布 对数正态分布 伽马分布 伽马函数 贝塔函数 伽马分布 卡方分布 F分布 t分布 附录 参考文献 本文主要介绍一些常见的分布&#xff0c;包括正态分布、对数正态分布、伽马分布、卡方分布、F分布、t分布。给出了分布的定义&#xff0c;推导了概率密度函数&…...

科技云报道:押注向量数据库,为时过早?

科技云报道原创。 在大模型的高调火热之下&#xff0c;向量数据库也获得了前所未有的关注。 近两个月内&#xff0c;向量数据库迎来融资潮&#xff0c;Qdrant、Chroma、Weaviate先后获得融资&#xff0c;Pinecone宣布1亿美元B轮融资&#xff0c;估值达到7.5亿美元。 东北证券…...

铭控传感亮相2023国际物联网展,聚焦“多场景物联感知方案”应用

金秋九月&#xff0c;聚焦IoT基石技术&#xff0c;荟萃最全物联感知企业&#xff0c;齐聚IOTE 2023第20届国际物联网展深圳站。铭控传感携智慧楼宇&#xff0c;数字工厂&#xff0c;智慧消防&#xff0c;智慧泵房等多场景物联感知方案及多品类无线传感器闪亮登场&#xff0c;现…...

前端demo: 实现对图片进行上传前的压缩功能

前端可以使用canvas和File API来对图片进行压缩和缩放处理&#xff0c;以下是一个示例代码 : 压缩方法compressImg这段代码是实现对图片进行上传前的压缩功能 1. 定义了一个压缩图片的函数 compressImg&#xff0c;接受两个参数&#xff1a;file表示要压缩的文件&#xff0c;q…...

计算机网络(文章链接汇总)

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 计算机网络&#xff08;一&#xff09;&#xff1a;概述计算机网络&#xff08;二&#xff09;&#xff1a;物理层计算机网络&#xff08;三&#xff09;&#xff1a;数据链路层计算机网…...

黑科技-Android

1热更新&#xff08;热修复&#xff09;&#xff1a;apk不用发版&#xff0c;就能修复bug 原理&#xff1a;我们修复好了bug的时候&#xff0c;把那些有改动的java源码编译成class&#xff0c;再打包成dex&#xff0c;然后通过反射技术放到dexElements数组的最前面&#xff0c;…...

450. 删除二叉搜索树中的节点

给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;删除节点可分为两个步骤&#xff1a; 首先…...

python安全工具开发基础

文章目录 拷贝、with、is深拷贝、浅拷贝with 三器一闭迭代器生成器闭包装饰器 动态绑定垃圾回收网络编程UdpTcp 协程mysql预处理防止注入 redis未授权/弱密码 拷贝、with 、is a [11, 22, 33] b [11, 22, 33] ca print(id(a)) print(id(b)) print(id(c))print(a b) print(…...

26 docker前后端部署

[参考博客]((257条消息) DockerNginx部署前后端分离项目(SpringBootVue)的详细教程_在docker中安装nginx实现前后端分离_这里是杨杨吖的博客-CSDN博客) (DockerNginx部署前后端分离项目(SpringBootVue)) 安装docker # 1、yum 包更新到最新 yum update # 2、安装需要的软件包…...

[linux] SFTP文件传输基本命令 --- xshell 直接上传文件

2.sftp - 上传文件&#xff1a;如果上传/下载的是文件夹, 在put/get命令后加上-r参数即可。 上传文件&#xff1a; 把本地服务器的/www/wwwroot目录下面的study.log文件上传到远程服务器的/www/server目录下。 sftp> lcd /www/wwwroot sftp> put study.log /www/server…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...