线程与进程、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多线程编程,这些就留着让你们自己去探索啦!