Android多线程概况

Android沿用了Java的线程模型,一个Android应用在创建的时候会开启一个线程,我们叫它主线程或者UI线程。如果我们想要访问网络或者数据库等耗时操作时,都会开启子线程去处理(为了避免主线程被耗时操作阻塞而产生ANR),也说明了多线程在Android应用开发占据这十分重要的地位。

1.线程基础

1.1、线程和进程

1.1.1.什么是进程

进程是操作系统结构的基础,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的基本单位。进程可以看作程序的实体,同样它也是线程的容器。

1.1.2.什么是线程

每个进程里运行了很多子任务,这些子任务就是线程。
一个进程中可以创建多个线程,这些线程拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

1.1.3.为何要使用多线程

在操作系统级别上来看主要有以下几个方面:
使用多线程可以减少程序的响应时间。如果某个操作很耗时,或者陷入长时间的等待,此时程序将不会响应用户的输入操作,使用多线程之后,可以把这个耗时操作分配到一个单独的线程中去执行,从而使程序具备了更好的交互性。

与进程相比,线程创建和切换开销更小,同时多线程在书架共享方面效率非常高

多CPU或者多核计算机本身就具备执行多线程的能力,如果使用单个线程无法充分利用计算机资源,这会造成资源的巨大浪费,在多CPU计算机中使用多线程能提高CPU的利用率。
使用多线程能简化程序的结构,使程序便于理解和维护。

1.2.主线程和子线程

主线程是指进程所拥有的线程。主线程主要处理界面交互相关的逻辑,因为用户随时会和界面发生交互,因此在任何时候都必须有较高的响应速度,否则就会产生界面卡顿的感觉。为了保持较高的响应速度,我们要求主线程中不能执行耗时的任务。

1.3.线程的状态

Java线程在运行的声明周期中可能会处于6种不同的状态,这6种线程状态分别为如下所示。

  • New:新创建状态。线程被创建,还没有调用start方法,在线程运行之前还有一些基础工作要做。
  • Runnable:可运行状态。一旦调用start方法,线程就处于Runnable状态。一个可运行的线程可能正在运行也可能不在运行,这取决于操作系统提供的运行时间。
  • Blocked:阻塞状态。表示线程被锁阻塞,它暂时不活动。
  • Waiting:等待状态。线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器重新激活它。
  • Timed waiting:超时等待状态。和等待不同的是,它是可以在指定的时间自行返回的。
  • Terminated:终止状态。表示当前线程已经执行完毕。导致线程终止有两种情况:1.run方法执行完毕正常退出;2.因为一个没有捕获的异常而终止了run方法,导致线程进入终止状态。

线程创建之后,调用Thread的start方法,开始进入运行状态,当线程执行wait方法后,线程进入等待状态,进入等待状态的线程需要其他线程通知才能返回运行状态。

1.4.创建线程

多线程的实现一般有以下3中方法,其中前两种最为常见:

1.4.1.继承Thread类,重写run()方法

Thread本质上也是实现了Runnable接口的一个实例。需要注意的是调用start()方法后并不是立即执行多线程的代码,而是使该线程变为可运行态,什么时候运行多线程代码是由操作系统决定的,以下是主要步骤:
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此,run()方法被称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程

public class TestThread extends Thread{
	public void run(){
		 System.out.println("Hello World");
	}
	public static void main(){
		Thread mThread=new TestThread();
		mThread.start();
	}

}
1.4.2.实现Runnable接口,并实现该接口的run()方法

一下是主要步骤:
(1).自定义类并实现Runnable接口,实现run()方法。
(2)创建Thread子类的实例,用实现Runnable接口的对象作为参数实例化该Thread对象
(3)调用Thread的start()方法来启动该线程。

public class TestRunnable implements Runnable{
	public void run(){
	System.out.println("Hello World");
	}
}

public class TestRunnable{
	public static void main(String[] args){
		TestRunnable mTestRunnable = new TestRunnable();
		Thread mThread = new Thread(mTestRunnable);
		mThread.start();
	}

}
1.4.3.实现Callable接口,重写call()方法

Callable接口实际是属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但是提供了比Runnable更强大的功能,主要表现为以下3点:
(1)Callable可以在任务接受后提供一个返回值,Runnable无法提供这个功能。
(2)Callable中的call方法可以抛出异常,而Runnable无法提供这个功能.
(3)运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下就可以使用Future来监视调用call()方法的情况。但调用Future的get()方法以获取结果时,当前线程就会阻塞,直到call()返回结果。

public class TestCallable{
	//创建线程类
	public static class MyTestCallable implements Callable{
		public String call() throws Exception{
			return "Hello World";
		}
	}
	public static void main(String[] args) {
		MyTestCallable mMyTestCallable = new MyTestCallable();
		ExecutorService mExecutorService.submit(mMyTestCallable);
		try{
			//等待线程结束,并返回结果
			System.out.println(mfuture.get());
		}catch(Exeption e){
			e.printStackTrace();
		}
	}

}

在这3种方式中,一般推荐实现Runnabale接口的方式,原因是:一个类应该在其需要加强或者修改的时候才会被继承。因此,如果没必要重写Thread类的其他方法,最好用实现Runnable

1.5理解中断

当线程的run方法执行完毕,或者在方法中出现没有捕获的异常时,线程将终止。在Java早期版本中有一个stop()方法,其他线程可以调用它终止线程,但是这个方法以及被弃用了。interrupt方法可以用来请求中断线程。当一个线程调用interrupt方法时,线程的中断标识位将被置位(中断标志为true),线程会不断地检测这个中断标识位,以判断线程是否应该被中断。要想知道线程是否被置位,可以调用Thread.currentThread().isInterrupted(),如下所示:

while(!Thread.currentThread().isInterrupted){
	//do something
}

还可以调用Thread.interrupted()来中断标识位进行复位。但是如果一个线程被阻塞,就无法检查中断状态。如果一个线程处于阻塞状态,线程在检查中断标识位的时候如果发现中断标识位为true,则会在阻塞方法调用处抛出InterruptedException异常,并且在抛出异常前将线程的中断标识位复位,即重新设置为false。需要注意的是被中断的线程不一定会终止,中断线程是为了引起线程的注意,被中断的线程可以决定如何去响应中断。如果是比较重要的线程,则不会理会中断,而大部分情况则是线程会将中断作为一个终止的请求。另外,不要在底层代码里捕获InterruptedException异常后不做处理,如下:

void myTask(){
	...
	try{
		sleep(50);
	}catch(InterruptedException e){
	
	}
	...
}

如果你不知道抛出InterruptedException异常后如何处理,这里介绍两种合理的处理方式。
(1)在catch子句中,调用Thread.currentThread.interrupt()来设置中断状态(因为抛出异常后中断标识位会复位),让外界通过判断Thread.currentThread().isInterrupted()来决定是否终止线程还是继续下去,应该这样做:

void myTask(){
	...
	try{
		sleep(50);
	}catch(InterruptedException e){
		Thread.currentThread().interrupted();
	}
	...
}

(2)更好的做法就是,不使用try来捕获这样的异常,让方法直接抛出,这样调用者可以捕获这个异常,如下所示:

void myTask() throw InterruptedException{
	sleep(50);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值