Android中Handler 、Thread和Runnable
在多线程编程的时候,我们经常会用到Handler,Thread和Runnable这三个类,我们来看看这三个类之间是怎么样的关系?
首先说明Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。 而Runnable是一个接口,不会自己开启一个线程,依旧运行在UI线程,Thread是Runnable的子类,实现Runnable接口来开启线程是把Runnable对象至于Thread中运行。
1.Handler
1.1Handler定义
主要接受子线程发送的数据, 并用此数据配合主线程更新UI ,或者利用post,postdelay方法来实现延时操作
解释:Handler可以分发Message对象和Runnable对象到主线程中,每个Handler实例都会绑定到创建他的线程中(一般是主线程)
1.2.Handler方法
Handler中分发消息的一些方法 post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable long) sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message,long) sendMessageDelayed(Message,long) removecallbacks(Runnable) 方法postdelay的作用是将Runnable里面的run方法延迟后运行 方法removecallbacks方法是删除指定的Runnable对象
2.Runnable和Thread
我们常说开启线程的方法有两个,一:实现Runnable接口;二:继承Thread类。但无论怎么样,开启线程都是通过Thread开启,Runnable并不能开启线程,实现Runnable接口只是作为Thread对象的参数来开启线程。
Runnable就相当于一个作业,而Thread才是真正的处理线程。
2.1Runnable和Thread的区别
首先阐述实现Runnable的好处:
●java不允许多继承,因此实现了Runnable接口的类可以再继承其他类。
●方便资源共享,即可以共享一个对象实例???(从很多博客中看到这样描述,但是此处有疑问,例子如下)(PS:继承Thread开启线程也可以资源共享,但是容易出现异常)
使用Thread不能资源共享实例:
public class MyThreadWithExtends extends Thread {
private int tickets = 10;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"--卖出票:" + tickets--);
}
}
}
public static void main(String[] args) {
MyThreadWithExtends thread1 = new MyThreadWithExtends();
MyThreadWithExtends thread2 = new MyThreadWithExtends();
MyThreadWithExtends thread3 = new MyThreadWithExtends();
thread1.start();
thread2.start();
thread3.start();
//每个线程都独立,不共享资源,每个线程都卖出了10张票,总共卖出了30张。如果真卖票,就有问题了。
}
}
运行结果:
Thread-0--卖出票:10
Thread-2--卖出票:10
Thread-1--卖出票:10
Thread-2--卖出票:9
Thread-0--卖出票:9
Thread-2--卖出票:8
Thread-1--卖出票:9
Thread-2--卖出票:7
Thread-0--卖出票:8
Thread-2--卖出票:6
Thread-2--卖出票:5
Thread-2--卖出票:4
Thread-1--卖出票:8
Thread-2--卖出票:3
Thread-0--卖出票:7
Thread-2--卖出票:2
Thread-2--卖出票:1
Thread-1--卖出票:7
Thread-0--卖出票:6
Thread-1--卖出票:6
Thread-0--卖出票:5
Thread-0--卖出票:4
Thread-1--卖出票:5
Thread-0--卖出票:3
Thread-1--卖出票:4
Thread-1--卖出票:3
Thread-1--卖出票:2
Thread-0--卖出票:2
Thread-1--卖出票:1
Thread-0--卖出票:1
使用Thread能资源共享实例:
public class TicketThread extends Thread{
private int ticket = 10;
public void run(){
for(int i =0;i<10;i++){
synchronized (this){//解决每个线程共享了对象myRunnable的资源,卖出的总票数是对的,但是顺序是乱的问题
if(this.ticket>0){
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"卖票---->"+(this.ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] arg){
TicketThread t1 = new TicketThread();
new Thread(t1,"线程1").start();
new Thread(t1,"线程2").start();
//也达到了资源共享的目的,此处网上有各种写法,很多写法都是自圆其说,举一些特殊例子来印证自己的观点,然而事实却不尽如此。
}
}
运行结果
线程1卖票—->10
线程1卖票—->9
线程1卖票—->8
线程2卖票—->7
线程2卖票—->6
线程1卖票—->5
线程1卖票—->4
线程2卖票—->3
线程2卖票—->2
线程1卖票—->1
实现Runnable接口开启线程两种方式都能实现资源共享
2.2总结:在程序开发中只要是多线程肯定永远以实现Runnable接口为主。
实现Runnable接口相比继承Thread类有如下好处:
1、避免继承的局限,一个类可以继承多个接口。
2、适合于资源的共享。
3线程池
既然实现了多线程那必然离不开管理这些线程,当问题比简单时一个或者几个线程就OK了,也涉及不到效率问题。一旦线程数
量多起来的时候,必然躲不过这些线程的创建与销毁,而往往这是很浪费时间的。这时就需要利用线程池来进行管理,既免去了>我们创建线程和销毁线程的代码,也提高了程序的效率。
3.1为什么使用线程池
1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率
例如:
记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3
如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!
正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销
2.线程并发数量过多,抢占系统资源从而导致阻塞
我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况
运用线程池能有效的控制线程最大并发数,避免以上的问题
3.对线程进行一些简单的管理
比如:延时执行、定时循环执行的策略等
运用线程池都能进行很好的实现