您的位置:首页 > 移动开发 > Android开发

Java中的多线程Thread Runnable及android的handler

2015-10-25 12:52 489 查看
1.在java中,多线程一般有两种方式。简言之:一个是继承Thread类,另一个是实现runnable接口。他们之间的区别主要是在于,对于同时开启的多个对象去启动多线程的时候,继承Thread的各个对象之间不能实现数据的共享,而runnable可以。最经典的例子就是买票系统的实现。大家可以百度代码。

android 的多线程实际上就是java的多线程。android的UI线程又称为主线程。

Thread 和 Runnable:

Thread是一个线程,而Runnable可以理解为一个任务。这个任务只是一个接口。具体的任务执行是在 run()方法执行。

Thread thread = new Thread(Runnable);

把一个Runnable任务放到线程里面,当调用thread.start() 的时候,系统新开一个线程去执行,这个runnable任务是在多线程执行的。是在新开的线程执行的。

如果直接 new Runnable().run();那么实际上是直接在UI线程执行了这个任务没有进行一个多线程的操作。

总结:runnable()只是一个任务的抽象,并不是多线程。Thread.start()。才是新开一个多线程。并且在新开的线程执行Thread你们的run()方法。

2.继承和实现都需要重写里面的run方法,该方法就是你子线程运行的逻辑方法,同时new出来的对象也需要运行.start方法。特别要注意的是,实现runnable接口的new 出来的对象之后,其实此时根本没有产生一个多线程,我们仍然需要new Thread(new runnable)对象,然后将实现runnable接口对象作为Thread类一个构造方法的参数传进去。

2.1复制别人的代码。继承Thread类

<span style="font-size:18px;">package org.thread.demo;
class MyThread extends Thread{
private String name;
public MyThread(String name) {
super();
this.name = name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println("线程开始:"+this.name+",i="+i);
}
}
}
/* package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
mt1.run();
mt2.run();
}
} *///但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。还是单线程此时
//在JDK的文档中可以发现,一旦调用start()方法,则会通过JVM找到run()方法。下面启动start()方法启动线程:
package org.thread.demo;  
public class ThreadDemo01 {  
public static void main(String[] args) {  
MyThread mt1=new MyThread("线程a");  
MyThread mt2=new MyThread("线程b");  
mt1.start();  
mt2.start();  
}  
}; //这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?
//在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,

</span>


2.2实现runnable接口

<span style="font-size:18px;">public interface Runnable{
public void run();
}
例子:
package org.runnable.demo;
class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this.name = name;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println("线程开始:"+this.name+",i="+i);
}
}
}; </span>
但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):

<span style="font-size:18px;">package org.runnable.demo;
import org.runnable.demo.MyThread;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
new Thread(mt1).start();
new Thread(mt2).start();
}
} </span>
此时thread对象没有被实现,不要和第一种方式中thread之类搞糊涂了。比较推荐使用后一种方法。

3.handler(和线程,Loop一一对应)。

Handler中分发消息的一些方法

[b]       sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.      

           sendEmptyMessage(int)

           sendMessage(Message)

           sendMessageAtTime(Message,long)

           sendMessageDelayed(Message,long)
[/b]

       post类方法允许你排列一个Runnable对象到主线程队列中

           post(Runnable)

           postAtTime(Runnable,long)

           postDelayed(Runnable long)      


3.1handler是android中不同线程之间传递消息的机制。最常用的是用来更新系统的UI,因为UI操作只能在Main线程中执行,所以我们一旦在进行比如联网下载比较耗时的操作时,如果是在主线程中,就会报NRA错误,这对用户体验十分不友好,所以,此时如果我们有个在main线程中的handler对象,然后在子线程中进行耗时操作,结束后通过hangdler通知,再在main中更新就可以了。下面我们从handler的各种方法入手

send:(子线程向绑定handler中的main线程发消息)

首先new handler()对象,重写其handleMessager方法,该handler对象默认是在main中

3.1.1     handler.sendEmptyMessage(int what);该方法是发送空消息,用int型what参数区分不同的消息,同理,有其延迟方法    handler.sendEmptyMessageDelayed(what, delayMillis)。

3.1.2     handler.sendMessage(msg);发送消息msg,msg可以传递任意类型对象,同时其也有延迟方法方法。

<span style="font-size:18px;">public class ChangeTextActivity extends Activity {
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
num++;
tv.setText(num+"");
}
}
};
    TextView tv;
    int num=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);       
        tv = (TextView)findViewById(R.id.textView1);
        new Thread() {
        public void run() {                        
        handler.sendEmptyMessage(1);
        sleep(1000);}                               
}.start();
  }       
}
</span>


post:

3.2.1

public class QuesPostActivity extends Activity {
ImageView iv;
/*
* 通过post方法指定要执行的动作
* 如果是通过post方法指定要执行的动作,那么
* Handler对象不需要再重写handleMessage方法
* */
Handler handler = new Handler();
Runnable runnable ;
Random ran;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView)findViewById(R.id.imageView1);
ran = new Random();
runnable = new Runnable() {
@Override
public void run() {
int red = ran.nextInt(256);
int green = ran.nextInt(256);
int blue = ran.nextInt(256);

iv.setBackgroundColor(Color.rgb(red, green, blue));
//指定在500毫秒之后,运行当前类对象的run方法
handler.postDelayed(this, 500);
}
};
/*
* 一旦运行到post方法,那么马上执行该参数中的Runnable对象的run方法
* 并且注意:此处的Runnable对象中的run方法内可以调用改变UI的代码
* */
handler.post(runnable);
}
public void click (View v) {
//点击按钮停止颜色的切换
/*
* 停止参数指定的runnable对象的run方法的运行
* */
handler.removeCallbacks(runnable);
}
}
该代码和注释比较完整的说明了post的一个用法,首先,post中的关键参数就是一个实现了runnable的子类对象,这里是内部类来实现的,在该之类中run方法中写上逻辑,为什么此时的run方法可以更新UI呢,其实runna接口在没有Thread启动时,还是在当前
线程也即main中的,所以这里最好不要进行一些耗时的操作。同时其也有延时方法,以及去runnable对象方法。可以看到并没有多线程参与。

具体来说,这个函数的工作原理如下:
View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。

3.3HandlerThread(是用来产生子线程的,thread子类)

该类可以用来实现main线程中向子线程发送消息,此时最重要的就是在子线程中有一个handler对象以及loop对象(mian中的loop对象自己生成,不需要get)

步骤:

创建一个
HandlerThread
,即创建了一个包含Looper的线程。

HandlerThread handlerThread = new HandlerThread("leochin.com");
handlerThread.start(); //创建HandlerThread后一定要记得start()

获取
HandlerThread
的Looper

Looper looper = handlerThread.getLooper();//获得该子线程的loop对象。

创建Handler,通过Looper初始化

Handler handler = new Handler(looper);//将该子线程中的loop对象绑定到handler中,此时的handler就是该子线程的handler,可以在main中调用发送消息。

通过以上三步我们就成功创建
HandlerThread
。通过handler发送消息,就会在子线程中执行。

如果想让
HandlerThread
退出,则需要调用
handlerThread.quit();


此时可以实现不同线程之间进行消息的传递了。

4.下图为handler在不同线程间消息传递的机制。

4.1首先明确,handler和loop需要有自己对应的线程,如下图,为main线程中

4.2其他线程通过main的handler对象将消息封装并存入main的MessgerQuuen中,通过main的loop对象来进行管理

4.3main的handler通过handleMeeager将消息取出在main中处理






一下代码可以加深对Loop的理解

<span style="font-size:18px;">new Thread() {
public void run() {
Looper.prepare();//在该子线程中生成其对应的loop对象
handler = new Handler(){
public void handleMessage(android.os.Message msg) {
Log.i("=====", "=======子线程1"+Thread.currentThread().getName());
}
};
};
Looper.loop();//才能将消息循环起来其作用
};
}.start();</span>


很多理解都是自己的理解,希望对大家的理解有帮助。

相关链接:http://blog.chinaunix.net/zt/1026/androidhandlerrunnablehand_1026281.shtml
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: