http://www.jianshu.com/p/af255c526f7e
Thread类是真正的线程,查看源码可见Thread也实现了Runnable接口,但它内部有创建新的工作线程,所以Thread对象运行在与主线程不一样的分线程上。
Thread主要函数:
Thread线程主要状态:
因为Thread对象运行在另外的线程,所以它与Runnable实例有如下主要区别:
因为Thread对象运行在另外的线程,所以它与Runnable实例有如下主要区别:
为加深对Thread类工作机制的了解,博主认真实践了网上广为流传的售票员线程例子,下面是Thread的三种调用方式:
public class TicketThread extends Thread {
private int ticketCount = 100;
@Override
public void run() {
for (; ticketCount>0; ticketCount--) {
String desc = String.format("%d %d 当前余票为%d张",
System.currentTimeMillis(), getId(), ticketCount);
System.out.println(desc);
}
}
}
public class ThreadTest {
public static void main(String[] arg) {
//方式一,同一Thread实例多次start,重复调用会抛出异常。因为start是同步方法,不允许同一时刻多次运行
// TicketThread seller = new TicketThread();
// seller.start();
// seller.start();
// seller.start();
//方式二,创建多个Thread实例分别start,三个线程每个各卖100张,总共卖了300张票
// new TicketThread().start();
// new TicketThread().start();
// new TicketThread().start();
//方式三,只创建一个Runnable实例,使用该实例启动三个线程一起卖票,总共卖了100张票
Runnable mSeller = new Runnable() {
private int ticketCount = 100;
@Override
public void run() {
for (; ticketCount > 0; ticketCount--) {
String desc = String.format("%d 当前余票为%d张",
System.currentTimeMillis(), ticketCount);
System.out.println(desc);
}
}
};
new Thread(mSeller).start();
new Thread(mSeller).start();
new Thread(mSeller).start();
}
}
其中方式三由于使用的是Runnable实例,故而无法打印线程号,无法确定是多线程交替卖票。但可通过下列手段进行观察:
Handler用于UI线程与分线程之间的通信,分线程利用Handler实例向UI线程发送消息,UI线程收到消息后在Handler对象中进行处理。下面是Handler的常用方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
上面对dispatchMessage的说明很容易让人犯糊涂,我们再看一下postDelayed方法的源码,我们知道,Handler的postDelayed方法可用于启动Runnable对象:
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
从以上源码可以看出,postDelayed方法中把要启动的Runnable对象给设置到Message回调变量中,这样主线程处理消息时调用的是该Runnable对象,而不是自身的handleMessage方法,所以启动Runnable不需要重写handleMessage方法,而Thread启动就要重写handleMessage方法。
obtainMessage : 获取当前消息的对象 sendMessage : 向主线程发送消息 sendMessageDelayed : 延迟一段时间向主线程发送消息 sendMessageAtTime : 在指定时间向主线程发送消息 sendEmptyMessage : 向主线程发送空消息 sendEmptyMessageDelayed : 延迟一段时间向主线程发送空消息 sendEmptyMessageAtTime : 在指定时间向主线程发送空消息 removeMessages : 从消息队列中根据消息标识移除指定消息 hasMessages : 判断消息队列中是否存在指定消息标识的消息
总结一下Handler的消息流转流程:
Looper类主要是对消息队列MessageQueue进行管理,一般情况下不用关心。有用到的话,就是在构造Handler时传入指定的Looper对象。
Message类是Handler机制中存放消息的包裹,其作用类似于组件通信Intent机制中的Bundle类。Message类的主要参数说明如下: what : 整型的消息标识,在Handler的removeMessages和hasMessages方法中作为消息判断的唯一标识 arg1 : 整型数,可存放消息的处理结果 arg2 : 整型数,可存放消息的处理代码 obj : Object类型,可存放消息传递的数据结构 replyTo : Messenger类型,回应信使,在跨进程通信中使用,线程间通信用不着
获取一个Message有两种方式:
Handler mHandler = new Handler();
Message msg = mHandler.obtainMessage();
Handler mHandler = new Handler();
Message msg = Message.obtain(mHandler);
Thread+Handler的示例代码如下:
public class ThreadActivity extends Activity implements OnClickListener {
private TextView tv_thread;
private long mBeginTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
Button btn_single = (Button) findViewById(R.id.btn_single);
Button btn_multi = (Button) findViewById(R.id.btn_multi);
btn_single.setOnClickListener(this);
btn_multi.setOnClickListener(this);
tv_thread = (TextView) findViewById(R.id.tv_thread);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_single) {
mBeginTime = System.currentTimeMillis();
new TicketThread().start();
} else if (v.getId() == R.id.btn_multi) {
mBeginTime = System.currentTimeMillis();
new Thread(mSeller).start();
new Thread(mSeller).start();
new Thread(mSeller).start();
}
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
long cost = System.currentTimeMillis() - mBeginTime;
String desc = String.format("消息标识%d:%s耗时%d毫秒",
msg.what, (String)(msg.obj), cost);
tv_thread.setText(desc);
}
};
private Runnable mSeller = new Runnable() {
private int ticketCount = 10000;
@Override
public void run() {
for (; ticketCount > 0; ticketCount--) {
String desc = String.format("%d 当前余票为%d张",
System.currentTimeMillis(), ticketCount);
System.out.println(desc);
}
if (ticketCount <= 0) {
Message msg = Message.obtain(mHandler);
msg.obj = "多个窗口卖票";
mHandler.sendMessage(msg);
}
}
};
private class TicketThread extends Thread {
private int ticketCount = 10000;
@Override
public void run() {
for (; ticketCount>0; ticketCount--) {
String desc = String.format("%d %d 当前余票为%d张",
System.currentTimeMillis(), getId(), ticketCount);
System.out.println(desc);
}
if (ticketCount <= 0) {
Message msg = mHandler.obtainMessage();
msg.obj = "单个窗口卖票";
mHandler.sendMessage(msg);
}
}
}
}