目录
一、什么是线程?
1、进程和线程的区别
2、如何创建线程?创建线程的几种方式?
3、Android线程间通信机制?
(1)通过单向数据管道传递数据
(2)共享内存通信
(3)实现消费者 - 生产者模式 BlockingQueue
(4)消息队列
4、什么是ANR?
二、线程的开启
1、线程的状态:
2、子线程开启的两种方式:
(1)扩展Thread类实现开启子线程
(2)实现Runnable接口
三、线程间的通信
1、线程间通信相关的组件
2、线程间的通信
3、线程发送消息的步骤
4、线程间的通信
(1)主线程向子线程发送消息
(2)子线程向主线程发送消息
5、线程间多个参数的传递
6、通过adb命令查看线程信息
一、什么是线程?
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程是独立调度和分派的基本单位。
同一进程中的多条线程将共享该进程中的全部系统资源。
一个进程可以有很多线程,每条线程并行执行不同的任务。
1、进程和线程的区别
进程是资源管理的最小单位,线程是程序执行的最小单位
每个进程都有自己的数据段 代码段 和堆栈段。线程 通常叫做 轻型的进程。它包含独立的栈和CPU寄存状态,每个线程共享其所附属进程的所有资源,包含打开的文件,内存页面,信号标识及分配内存等
线程比进程花费更小的CPU资源
在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持多处理器,并且减少进程上下文切换的开销。
线程和进程的关系是 线程属于进程,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出锁产生的线程会被强制退出并清楚,一个进程至少需要一个线程来作为他的指令执行体,进程管理着资源(比如CPU,内存,文件等等)。而将线程分配到某个CPU上运行
线程按照其调度者可分为用户级线程和内核级线程两种。
用户级线程:主要解决的是上下文切换的问题,其调度过程由用户决定(程序开发者)
内核级线程:又内核调度实现
用户级线程要绑定内核级线程使用
默认情况下不能保证新线程和调用线程的执行顺序 (比如:赛跑)
每个进程内部的不同线程都有自己的唯一标识(ID),线程标识只在它所属的进程环境中有效
Sleep线程告诉系统,自己再一段时间内不被调度。
2、如何创建线程?创建线程的几种方式?
创建线程的几种方式:https://blog.csdn.net/qq_36408196/article/details/90452194
3、Android线程间通信机制?
(1)通过单向数据管道传递数据
管道(使用PipedWriter/ 创建PipedReader)是java.io包的一部分。也就是说,它们是一般的Java功能,而不是Android特定的。管道为同一进程内的两个线程提供了一种连接和建立单向数据通道的方法。生产者线程将数据写入管道,而使用者线程从管道读取数据。
共享内存(使用编程中已知的内存区域作为堆)是在线程之间传递信息的常用方法。应用程序中的所有线程都可以访问进程中的相同地址空间。
(3)实现消费者 - 生产者模式 BlockingQueue
线程信令是一种低级,高度可配置的机制,可以适应许多用例,但它也可能被认为是最容易出错的技术。因此,Java平台在线程信令机制上构建高级抽象,以解决线程之间的任意对象的单向切换。抽象通常被称为“解决生产者 - 消费者同步问题。”问题包括可能存在生成内容的线程(生产者线程)和消费内容的线程(消费者线程)的用例。生产者将消息传递给消费者进行处理。线程之间的中介是具有阻塞行为的队列,即java.util.concurrent.BlockingQueue。
消息处理机制是Android平台的基础,API位于android.os包中,其中包含一组实现该功能的类Handler。
android.os.Looper
与唯一的消费者线程关联的消息调度程序。
android.os.Handler
消费者线程消息处理器,以及生产者线程将消息插入队列的接口。Looper可以有许多关联的处理程序,但它们都将消息插入到同一队列中。
android.os.MessageQueue
要在消费者线程上处理的无限链接消息列表。每个Looper和Thread-最多只有一个MessageQueue。
android.os.Message
要在使用者线程上执行的消息。
4、什么是ANR?
应用程序无响应对话框ANR
- 主要按键类型或者触摸特定事件5s内无响应
- BroadcastReceiver在特定时间内10s无法处理完成
- 小概率service在特定时间内无法处理完成
解决办法:开启子线程
二、线程的开启
1、线程的状态:
创建满足条件 进入就绪状态 获得CPU执行时间 运行 异常中断或者执行完毕 线程消亡
注意:CPU在一个时刻只能运行一个线程 切换线程时会保存线程状态
多线程会造成系统资源占用增加Thread类实现了Runnable接口
2、子线程开启的两种方式:
- 扩展java.lang.Thread类
- 实现Runnable接口
(1)扩展Thread类实现开启子线程
Thread类实现了Runnable的接口
public class Thread implements Runnable {
private final Object lock = new Object();
private volatile long nativePeer;
boolean started = false;
private volatile String name;//线程名字
private int priority;//线程优先级
private java.lang.Thread threadQ;
private long eetop;
private boolean single_step;
private boolean daemon = false;//线程是否是一个守护线程
private boolean stillborn = false;
private Runnable target;//线程执行的任务
start();//启动线程
run();//线程执行的任务,继承Thread类必须重写run()方法
sleep();//线程休眠
yleld();//让当前线程交出CPU权限,让cpu去执行其他线程,但是 yleld()不能控制交出CPU的时间,同时只能让拥有相同优先级的线程获取CPU执行时间的机会
与sleep()不同的是yleld()方法不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需重新等待获取CPU执行时间
join();//如果在main()线程中调用thread.jion(),则main()线程会等待thread线程执行完毕,在执行main()线程;调用thread.jion(毫秒),则main()线程会等待thread线程对应的时间,在执行main()线程;
interrupt();//单独调用interrupt()方法可以使得处于阻塞状态的线程抛出一个异常,用来中断一个正处于阻塞状态的线程;通过interrupt方法和isInterrupted()方法来停止正在运行的线程
currentThread();//获取线程的一些信息
}
- 新建类继承Thread类
- 重写Run()方法
private class MyThread extends Thread{
@Override
public void run() {
super.run();
}
}
- 在需要耗时操作的界面调用Thread类
MyThread myThread=new MyThread();
myThread.start();
(2)实现Runnable接口
- 自定义类实现Runnable()接口
private class MyRunnable implements Runnable{
@Override
public void run() {
}
}
- 在需要耗时操作的界面调用Runnable类
MyRunnable myRunnable=new MyRunnable();
new Thread(myRunnable,"自定义Runnable").start();
三、线程间的通信
1、线程间通信相关的组件
Handler:Message 的发送和处理
MessageQueue:存放Handler发送的Message消息队列,按照先入先出规则执行 创建一个MessageQueue会创建一个Looper Message:存储消息
Looper:不断从MessageQueue中抽取Message进行执行,一个Message需要一个Looper
Thread:负责调度整个消息循环,即Handler、MessageQueue、Looper全部在Thread里完成
2、线程间的通信:
Looper.prepare():初始化消息队列的一些资源参数
Looper.loop():开启消息队列
3、线程发送消息的步骤
- message存储需要发送的信息
Message message=new Message();
- handle发送信息
mHandler.sendMessage(message);
- 开启线程
mThread=new Thread();
mThread.start();
- 线程接收消息
mHandler=new Handler(
){};
Handler里实现handleMessage
mHandler =new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 取出msg
}
};
4、线程间的通信
(1)主线程向子线程发送消息
1、主线程发送消息,子线程的开启
Message message=new Message();
message.what=1;//存储消息
childHandler.sendMessage(message);//发送消息
MyThread myThread=new MyThread();
myThread.start();//开启线程
2、子线程接收消息:
private class MyThread extends Thread{
@Override
public void run() {
super.run();
childHandler =new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int myMyMessage=msg.what;//接收发送过来的消息
}
};
}
}
(2)子线程向主线程发送消息
1、子线程发送消息
private class MyThread extends Thread{
@Override
public void run() {
super.run();
childHandler =new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String messageTag= "这是我发送的消息";
Message message=new Message();
//Message msg,该msg用于存储接收的信息
//该message用于存储发送的信息
message.obj=messageTag;
if(mainHandler!=null){
mainHandler.sendMessage(message) ;
}
}
};
}
2、主线程接收信息
mainHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String childMsg= (String) msg.obj;
}
};
MyThread myThread=new MyThread();
myThread.start();
5、线程间多个参数的传递
Message msg = new Message()
msg.what = xxx;
msg.arg1 = xxx;
msg.arg2 = xxx;
msg.obj = xxx;
handler.sendMessage(msg);
调用多个Message msg = handler.obtainMessage().sendToTarget()传递多个msg,然后利用what的值过滤msg,接收不同的msg
注意:这里的Message 已经不是 new Message()了,而是从MessagePool 拿的,省去了创建对象申请内存的开销。
参考:https://blog.csdn.net/joebaby_/article/details/7876845
6、通过adb命令查看线程信息
通过命令行获取线程信息:https://blog.csdn.net/manoel/article/details/39991839
adb shell ps命令查看进程id、查看进程内的线程、以及如何过滤:https://blog.csdn.net/cadi2011/article/details/51565218