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

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中分发消息的一些方法

       sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.      
           sendEmptyMessage(int)
           sendMessage(Message)
           sendMessageAtTime(Message,long)
           sendMessageDelayed(Message,long)

       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)

步骤:

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

    HandlerThread handlerThread = new HandlerThread("leochin.com");

    handlerThread.start(); //创建HandlerThread后一定要记得start()

  2. 获取HandlerThread的Looper

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

  3. 创建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中处理

http://files.colabug.com/forum/201404/02/181239r2h19922p81y9u72.jpg


一下代码可以加深对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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值