Thread概述

Thread类是真正的线程,查看源码可见Thread也实现了Runnable接口,但它内部有创建新的工作线程,所以Thread对象运行在与主线程不一样的分线程上。

因为Thread对象运行在另外的线程,所以它与Runnable实例有如下主要区别:
1、Thread对象可进行网络通信,而非Thread方式的Runnable实例不可进行网络通信。因为Android要求UI线程不能访问网络,所以运行在UI线程上的Runnable也就不能访问网络。
2、因为Android要求只有UI线程才能操作页面视图,所以运行在UI线程上的Runnable可直接访问视图或控件,而Thread对象不可直接访问视图或控件。Thread对象若想访问,只能发送消息通知Handler对象去操作UI。

售票员线程

为加深对Thread类工作机制的了解,博主认真实践了网上广为流传的售票员线程例子,下面是Thread的三种调用方式:
1、同一Thread实例多次start,重复调用会抛出异常。因为start是同步方法,不允许同一时刻多次运行;
2、创建多个Thread实例分别start,三个线程每个各卖100张,总共卖了300张票;
3、只创建一个Runnable实例,使用该实例启动三个线程一起卖票,总共卖了100张票;

线程类的代码如下:

public class TicketThread extends Thread {private int ticketCount = 100;@Overridepublic 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;@Overridepublic 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实例,故而无法打印线程号,无法确定是多线程交替卖票。但可通过下列手段进行观察:
1、从方式三的日志看出,当前余票数目不是连续递减的,间接证明存在多线程的情况;
2、在日志中打印时间戳,可发现方式三的耗时要小于方式一(大约是二分之一不到)。方式一已经明确是单线程,那么方式三加快速度的原因也只能是多线程工作了。

Handler

Handler用于UI线程与分线程之间的通信,分线程利用Handler实例向UI线程发送消息,UI线程收到消息后在Handler对象中进行处理。下面是Handler的常用方法:

1、在UI线程中运行,只能重写的方法,不能直接调用:
handleMessage : 存放消息处理的具体逻辑
dispatchMessage : 一般不用重写。用来分发消息,如果消息中有设置Runnable对象,则消息交给Runnable对象处理;如果消息中未设置Runnable对象,则消息交给主线程处理。一般Thread线程不设置Runnable对象,下面是Android中dispatchMessage方法的源码:

    public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

上面对dispatchMessage的说明很容易让人犯糊涂,我们再看一下postDelayed方法的源码,在上节《 Android开发笔记(四十七)Runnable接口实现多线程》中我们知道,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方法。

2、在分线程中直接调用的方法:
obtainMessage : 获取当前消息的对象
post、postDelayed、postAtTime : 详细说明见《 Android开发笔记(四十七)Runnable接口实现多线程》
sendMessage : 向主线程发送消息
sendMessageDelayed : 延迟一段时间向主线程发送消息
sendMessageAtTime : 在指定时间向主线程发送消息
sendEmptyMessage : 向主线程发送空消息
sendEmptyMessageDelayed : 延迟一段时间向主线程发送空消息
sendEmptyMessageAtTime : 在指定时间向主线程发送空消息
removeMessages : 从消息队列中根据消息标识移除指定消息
hasMessages : 判断消息队列中是否存在指定消息标识的消息

总结一下Handler的消息流转流程:
1、Thread方式下运行:主线程创建Handler实例->启动分线程->分线程调用Handler实例的send系列方法->主线程分发消息dispatchMessage(消息中的Runnable对象为空)->主线程处理消息handleMessage。
2、Runnable方式下运行:主线程创建Handler实例->主线程调用Handler实例的post系列方法(内部其实是send方法,也就是说,主线程自己调用send方法)->主线程分发消息dispatchMessage(消息中的Runnable对象不为空)->Runnable对象启动运行。

Looper与Message

Looper类主要是对消息队列MessageQueue进行管理,一般情况下不用关心。有用到的话,就是在构造Handler时传入指定的Looper对象。

Message类是Handler机制中存放消息的包裹,其作用类似于组件通信Intent机制中的Bundle类。Message类的主要参数说明如下:
what : 整型的消息标识,在Handler的removeMessages和hasMessages方法中作为消息判断的唯一标识
arg1 : 整型数,可存放消息的处理结果
arg2 : 整型数,可存放消息的处理代码
obj : Object类型,可存放消息传递的数据结构
replyTo : Messenger类型,回应信使,在跨进程通信中使用,线程间通信用不着

获取一个Message有两种方式:
1、调用Handler的obtainMessage方法,示例代码如下:

Handler mHandler = new Handler();
Message msg = mHandler.obtainMessage();

2、调用Message的obtain方法,示例代码如下:

Handler mHandler = new Handler();
Message msg = Message.obtain(mHandler);

Thread+Handler的示例代码如下:

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;@SuppressLint("DefaultLocale")
public class ThreadActivity extends Activity implements OnClickListener {private TextView tv_thread;private long mBeginTime;@Overrideprotected 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);}@Overridepublic 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() {@Overridepublic 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;@Overridepublic 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;@Overridepublic 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);}}}
}

点此查看Android开发笔记的完整目录

Android开发笔记(四十八)Thread类实现多线程相关推荐

  1. Android开发笔记(十八)书籍翻页动画PageAnimation

    前面几节的动画都算简单,本文就介绍一个复杂点的动画--书籍翻页动画.Android有自带的翻页动画ViewPager,不过ViewPager只实现了平移效果.即便使用补间组合动画或者属性动画,也只是把 ...

  2. 【Visual C++】游戏开发笔记四十六 浅墨DirectX教程十四 模板测试与镜面特效专场

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处.   文章链接: http://blog.csdn.net/zhmxy555/article/details/8632184 作者:毛星云( ...

  3. Android开发笔记(十四)圆弧进度动画CircleAnimation

    一个好看的APP,都有不少精致的动画效果.熟练运用各种动画技术,可让我们的APP灼灼生辉.Android在技术上把动画分为了三类,分别是帧动画FrameAnimation.补间动画TweenAnima ...

  4. 【Visual C++】游戏开发笔记四十 浅墨DirectX教程之八 绘制真实质感的三维世界:光照与材质专场...

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 作者:毛星云(浅墨)    邮箱: happylifemxy@163.com 本篇文章里,我们对Direct3D之中固定功能流水线中的3D ...

  5. 【Visual C++】游戏开发笔记四十 浅墨DirectX教程之八 绘制真实质感的三维世界 光照与材质专场

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

  6. Android开发笔记(一百八十七)利用估值器实现弹幕动画

    如今上网看电影电视越发流行了,追剧的时候经常看到视频上方数行评论向左边飘去,犹如子弹那样飞快掠过,这些评论文字因此得名"弹幕".弹幕评论由正在观看的网友们即兴发表,故而连绵不绝从画 ...

  7. Android开发笔记(九十八)往图片添加部件

    添加圆角 添加圆角的功能,要用到Canvas类的drawRoundRect方法,即把画布裁剪成指定的圆角矩形. 下面是给图片添加圆角的效果截图: 下面是给图片添加圆角的代码片段: public sta ...

  8. Android开发笔记(十六)秋千摇摆动画SwingAnimation

    上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...

  9. Android开发笔记(十五)淡入淡出动画TransitionDrawable

    说到淡入淡出动画,可能大家会想到补间动画里面的AlphaAnimation,不过这个深浅动画只能对透明度做渐变效果,也就是只能对一个图形做深浅的颜色变换.如果我们想要从A图片逐渐变为B图片,也就是要实 ...

  10. Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

最新文章

  1. 智能问答在金融领域中的实践与应用
  2. ImportError: No module named 'keras.utils.visualize_util'
  3. 与计算机相关的课外活动,课外活动学生论文,关于应用型院校计算机专业课外活动相关参考文献资料-免费论文范文...
  4. ping 工具开发日记(1)
  5. gis导出栅格数据为什么不能tif_GIS基础操作教程(3)--点数据操作【附带练习数据】...
  6. 思科BFD协议帮助侦测网络失败
  7. 【机器学习】输出层的设计
  8. python毕业设计作品基于django框架校园排课选课系统毕设成品(6)开题答辩PPT
  9. 金蝶K3系统的网络服务端口
  10. 《Globally and locally consistent image completion》图像修复论文解读
  11. Java8新特性之Optional类(附代码案例)
  12. 第6章第6节:颜色搭配:配色万金油之色相配色方案 [PowerPoint精美幻灯片实战教程]
  13. 关于vue中采用scoped时,组件的中css优先级
  14. Python 完美诠释“高内聚“概念的 IO 流 API 体系结构
  15. 新型付费服务能否在IT领域异军突起?
  16. FoneDog iOS Toolkit(苹果数据恢复软件)官方正式版V2.1.62 | 苹果数据恢复大师下载 | 苹果数据恢复有免费的吗?
  17. Java中值得注意的『运算符、逻辑控制、输入输出』
  18. 解决Uncaught ReferenceError: $ is not defined报错
  19. 聊天服务器 单机性能,环信即时聊天服务器
  20. Windows7操作记录_操作时间记录_启动时间记录_日志查看

热门文章

  1. 蓝桥杯 基础练习 龟兔赛跑预测
  2. 吴恩达机器学习总结四:Octave语法
  3. 移动端车牌识别(前端识别、后端识别)的区别分析
  4. 记录一个美丽的小县城
  5. log4j 日志级别_log4j-Mybatis(5)
  6. CSS的Padding, Margin, Border 的区别
  7. java.sql.SQLException: Protocol violation 问题解析
  8. [设计模式-行为型]命令模式(Command)
  9. linux查看消息队列的状态,linux – 如何知道某个时间点在消息队列中收到的消息数...
  10. data后缀文件解码_小白学PyTorch | 17 TFrec文件的创建与读取