线程与进程、Android多线程编程
目录
- 一、概念
- 1.程序
- 2.进程
- 3.并发
- 4.并行
- 5.线程
- 二、Android中的进程与线程
- 1.安卓的进程
- 2.安卓的线程
- 三、Android多线程编程
- 1.线程的创建
- 1)继承Thread类
- 2)实现Runnable接口(常用)
- 2. 异步消息处理机制
- 1)Message
- 2)Handler
- 3)实战
一、概念
1.程序
程序是为了实现特定任务的一系列指令的有序集合,是存放在磁盘的可执行文件。它本身没有任何运行的含义,它只是一个静态的实体。
2.进程
进程是程序的一次执行过程,是一个动态的概念。
3.并发
并发是多个事件在同一时间间隔内发生。
并发是指一个处理器同时处理多个任务,在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
4.并行
并行是多个事件在同一时刻同时进行。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。作个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
5.线程
线程是进程的一部分,描述指令流执行状态,它是进程中的指令执行流的最小单位。线程是进程中的一个实体,一个进程包括1 ~ n个线程。若把进程称为任务的话,那么线程则是任务中的一个子任务的执行。一个进程中可以有多个线程,他们可以并发的执行多个任务。
为啥会有线程这种东西?因为有并发进行并且共享数据的这种需求;虽然进程也可以满足这种需求,但是它的开销很大(创建进程,进程结束,进程切换… …)。而线程能减少并发执行的时间和空间开销。
总结:
程序是静态的,进程是动态的
程序 = 文件(静态的可执行文件)
进程 = 执行中的程序 = 程序 + 执行状态
并发是一个人做多件事,来回切换。
并行是多个人做多件事,互不干涉。
并发是轮流处理多个任务,并行是同时处理多个任务。
一个进程中可以同时存在多个线程,各个线程之间可以并发执行。
二、Android中的进程与线程
1.安卓的进程
Android中有五种进程:前台进程,可见进程,服务进程,后台进程,空进程。
2.安卓的线程
前面我们讲到,一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。简单情况下,一个进程可以只有一个线程, 即主线程。当一个应用程序启动之后,android系统会为这个应用程序创建一个主线程(Main Thread),它负责渲染视图,分发事件到响应监听器并执行,对界面进行轮询的监听,所以在Android里面主线程也叫UI线程,在UI线程里才能操作界面。
三、Android多线程编程
如果我们在UI线程中做一些比较耗时的操作,比如访问网络或者数据库,都可能阻塞UI线程。对于用户来说,APP看起来像是卡住了,更坏的情况是,如果UI线程阻塞时间太长(超过5秒),Android系统会弹出ANR(application not responding)错误提示框。所以为了确保用户体验,主线程必须确保其响应速度,任何时候我们都不应该在主线程中处理非常耗时的任务,而子线程的作用就是完成耗时的操作,确保主线程的响应速度。
下面就让我们从线程的基本用法开始学起吧。
1.线程的创建
1)继承Thread类
class MyThread extends Thread{
@Override
public void run(){
//线程行为
}
}
//MyThread mt=new MyThread();
//mt.start();
new MyThread().start(); // 启动子线程,写在主线程中
步骤:
1、定义一个类继承Thread类,并重写Thread类的run()方法,run()方法的方法体就是线程要完成的任务;
2、创建该类的实例对象,即创建了线程对象;
3、调用线程对象的start()方法来启动线程,这样run()方法中的代码就会在子线程当中运行了。
注意,不是调用run()方法启动线程,run方法中只是定义需要执行的任务,如果调用run方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。
下面我们通过一个java例子更好地了解一下这种创建线程的方法吧
public class ThreadDemo1 {
public static void main(String args[]) { //执行main方法的线程就是主线程
Thread t = new MyThread (); // 实例化出一个线程对象
t.start();
}
}
// 继承Thread类
class MyThread extends Thread {
public void run() {
System.out.println("创建了一个新的线程");
}
}
2)实现Runnable接口(常用)
上一个方法虽然代码简单,但有很大的缺点,继承Thread类后就不能再继承别的类了,所以在开发中,优先选择实现Runable接口的方式。实现的方式没有类的单继承性的局限性,且更适合用来处理多个线程有共享数据的情况。
class MyThread implements Runnable {
@Override
public void run() {
//处理具体的逻辑
}
}
MyThread myThread = new MyThread();
new Thread(myThread).start();
步骤:
1、定义一个类实现Runnable接口;
2、创建该类的实例对象obj;
3、将obj作为构造函数参数传入Thread类实例对象,这个对象才是真正的线程对象;
4、调用线程对象的start()方法启动该线程。
同样来看个简单的java例子吧
public class ThreadDemo2 {
public static void main(String args[]) {
MyThread mt = new MyThread();
// mt.start(); //会报错,任务类MyThread中没有start()方法
// mt.run(); // 仅仅在主线程调用任务类MyThread中的run()方法,并没有新开一个线程
Thread t = new Thread(mt); // 传入任务类MyThread对象,实例化出一个线程对象
t.start(); // 调用线程对象的start()方法
}
}
//创建任务类,实现Runnable接口
class MyThread implements Runnable {
public void run() {
System.out.println("创建了一个新的线程");
}
}
如果不想专门再定义一个类去实现Runnable接口,也可以使用匿名类的方式,这种写法更为常见
new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();
2. 异步消息处理机制
现在我们都知道,耗时操作可以另外创建一个线程来运行以避免堵塞主线程。还有Android不允许在子线程中进行UI操作,因为Android的UI是线程不安全的(具体的原因和实践我在这里就不解释和进行了,想了解的可以上网查或看《第一行代码》)。但是有些时候,我们必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件,这该如何是好呢?
对于这种情况,Android提供了一套异步消息处理机制,完美地解决了这个问题,可以从子线程回到主线程中执行并带回结果。
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。 这里我主要介绍Message和Handler,其余的大家可以参考《第一行代码》进行学习。
1)Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
2)Handler
Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。Handler是主线程与子线程的通信媒介。
3)实战
现在我们通过一个具体的例子来实践一下如何使用Message和Handler。新建一个ThreadTest项目,然后修改activity_main.xml中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/change_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change text" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="30sp"/>
</LinearLayout>
布局文件中定义了两个控件,Button用于改变TextView中显示的内容,我们希望在点击Button后可以把TextView中显示的字符串改成Nice to meet you。
接着我们来修改MainActivity中的代码,如下所示:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private TextView text;
private static final int UPDATE_TEXT = 1; // 定义了一个整型常量 UPDATE_TEXT,用于表示更新TextView这个动作
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = findViewById(R.id.text);
Button changeText = findViewById(R.id.change_text);
changeText.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.change_text:
// 开启了一个子线程
new Thread(new Runnable() {
@Override
public void run() {
/* 创建了一个Message(android.os.Message)对象,并将它的what字段的值
指定为UPDATE_TEXT,然后调用Handler的sendMessage()方法将这条Message发送出去
*/
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
// 新增一个Handler对象,并重写父类的handleMessage()方法,在这里对具体的Message进行处理。
// 使用new handler()来实例化可能会出现警告,在参数中传入Looper.getMainLooper()可以确保Handler是在主线程中
private Handler handler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message message) {
/* Handler收到Message,并在handleMessage()方法中对它进行处理。
注意此时handleMessage()方法中的代码就是在主线程当中运行的了,所以我们可以在这里进行UI操作
*/
switch (message.what) {
case UPDATE_TEXT:
// 如果发现Message的what字段的值等于UPDATE_TEXT,就将TextView显示的内容改成Nice to meet you
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
}
使用步骤:
1、在主线程中创建一个Handler对象,重写它的handleMessage方法
2、在子线程中创建Message对象
3、调用Handler对象的sendMessage()方法发送Message
这里仅仅是为了举个例子而已,并没有涉及耗时操作,在实际开发过程中,在子线程中会进行一些耗时操作,如网络连接,下载文件,数据库操作等,然后根据这些耗时操作得到的结果来更新UI。
好啦,要讲的内容就那么多了,上面这些内容其实是Android 四大组件中服务的基础。Android/Java提供了很多类/接口来帮助大家完成异步操作,runOnUiThread()方法,AsyncTask抽象类也都常见于Android多线程编程,这些就留着让你们自己去探索啦!