android开发系列之多线程
今天在这篇博客里面,我只想谈谈自己对程序开发里面避无可避的一个问题-多线程的一些看法与思考。
其实说到多线程这个名称相信只要接触过软件这个行业的人都已经耳熟能详了,但是如果被问到到底什么才是多线程呢?为什么我们会需要多线程呢?多线程又会造成什么副作用呢?其实这些都应该是值得我们深入思考的一些问题,因为在多线程的环境里面,往往会发生一些意想不到的事情。下面让我们来针对android平台来具体看看多线程应该是怎么一回事呢?其实在android里面,系统已经为我们提供了一些封装好的多线程api,比如runable接口,AsyncTask后台任务等。
让我们先来看看一段代码,这段代码是比较常规的创建、开启一个线程的方法。
public class TestThread extends Thread { @Override
public void run() {
//do your something
}
}
上面方法只是定义好了一个线程,然后我们应该调用下面的方法运营它。
private void runMyThread(){
new TestThread().start();
}
这样的话,我们就相当于已经开启了一个线程。所以只要不手动中断这个线程的话,那么该线程就会在App处于运行态时一直跑。所以这个时候,如果我们有什么比较耗时的操作的话,我们就可以放在run方法里面执行了。下面就让我们来看看Thread里面的源码,看看为什么Thread调用start方法就会运行,为什么每个线程开启之后都会循环执行里面的run方法呢?有没有什么办法去中断一个线程呢?
首先我们来看看Thread的类定义,如下:
public class Thread implements Runnable{
}
可以看到Thread其实也是从Runable继承而来,那么什么是Runable呢?让我们稍后讲。一进到Thread类定义里面,给我们最直观的感受就是“volatile”,“ThreadLocal”,还有各种的Thread构造方法。我们可以看到在每个Thread构造方法里面都调用了create方法,相信看到这个名字大家就应该能够知道它是干什么用的吧,让我们截出create的源码来看看:
private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
Thread currentThread = Thread.currentThread();
if (group == null) {
group = currentThread.getThreadGroup();
} if (group.isDestroyed()) {
throw new IllegalThreadStateException("Group already destroyed");
} this.group = group; synchronized (Thread.class) {
id = ++Thread.count;
} if (threadName == null) {
this.name = "Thread-" + id;
} else {
this.name = threadName;
} this.target = runnable;
this.stackSize = stackSize; this.priority = currentThread.getPriority(); this.contextClassLoader = currentThread.contextClassLoader; // Transfer over InheritableThreadLocals.
if (currentThread.inheritableValues != null) {
inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
} // add ourselves to our ThreadGroup of choice
this.group.addThread(this);
}
从上面的源码我们可以很清楚的看到create方法里面就是设置线程所属的ThreadGroup,ThreadName,StackSize,但是有一点需要特别注意就是Runable对象,它是用来干什么的呢?我们通过代码查找,发现它主要是用在run方法里面,代码如下:
public void run() {
if (target != null) {
target.run();
}
}
我们可以看到Thread里面的run方法,其实是调用了Runable里面的方法。也就是说你可以在Thread实例化的时候,传进来一个Runable对象,这个时候就能执行到里面的run方法了。但是别急,如果你以为这样就能够真正创建一个线程的话,那么就错了。我们可以再看看start方法,也许你就能够明白了。
public synchronized void start() {
checkNotStarted(); hasBeenStarted = true; nativeCreate(this, stackSize, daemon);
} private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
看到了吗?其实在nativeCreate方法里面才会利用create方法里面设置的参数进行真正进行创建过程。现在让我们回到原来那个问题上,为什么线程创建之后,会一直执行run方法呢?具体怎么循环调用的,我在源码里面没有找到,也许是在c层调的吧。然后我们还可以看到常用的sleep方法,源码如下:
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("millis < 0: " + millis);
}
if (nanos < 0) {
throw new IllegalArgumentException("nanos < 0: " + nanos);
}
if (nanos > 999999) {
throw new IllegalArgumentException("nanos > 999999: " + nanos);
} // The JLS 3rd edition, section 17.9 says: "...sleep for zero
// time...need not have observable effects."
if (millis == 0 && nanos == 0) {
// ...but we still have to handle being interrupted.
if (Thread.interrupted()) {
throw new InterruptedException();
}
return;
} long start = System.nanoTime();
long duration = (millis * NANOS_PER_MILLI) + nanos; Object lock = currentThread().lock; // Wait may return early, so loop until sleep duration passes.
synchronized (lock) {
while (true) {
sleep(lock, millis, nanos); long now = System.nanoTime();
long elapsed = now - start; if (elapsed >= duration) {
break;
} duration -= elapsed;
start = now;
millis = duration / NANOS_PER_MILLI;
nanos = (int) (duration % NANOS_PER_MILLI);
}
}
}
我们可以看到在java这层只是做了一些时间上面的预设判断,真正的sleep实现是调用c层代码:
private static native void sleep(Object lock, long millis, int nanos);
最后让我们再来看看线程中断的方法:
public void interrupt() {
// Interrupt this thread before running actions so that other
// threads that observe the interrupt as a result of an action
// will see that this thread is in the interrupted state.
nativeInterrupt(); synchronized (interruptActions) {
for (int i = interruptActions.size() - 1; i >= 0; i--) {
interruptActions.get(i).run();
}
}
} private native void nativeInterrupt();
看到上面的代码其实有一点不解的就是,是不是意味着我中断当前的一个线程,系统就会默认启动后一个线程呢?
好了,接下来我们可以再看看Runable,其实可以发现它就是一个接口,里面提供了一个run方法,代码如下:
public interface Runnable { /**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
public void run();
}
最后我们来看看AsyncTask方法,它的特点是什么?我们应该怎么来使用它呢?首先我们来看看在android代码里面通常的使用方法是:
public class TestAsyncTask extends AsyncTask<String,Object,String> {
@Override
protected String doInBackground(String... params) {
return null;
} @Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
} @Override
protected void onProgressUpdate(Object... values) {
super.onProgressUpdate(values);
}
}
定义一个类,然后继承AsyncTask复写里面的三个方法。下面就让我们来具体看看AsyncTask的类定义:
public abstract class AsyncTask<Params, Progress, Result> {
}
我们可以看到AsyncTask是一个抽象类,同时利用泛型的方式定义三个参数,分别代表传入的参数、执行过程的进度、执行之后的结果。那么在AsyncTask里面又是怎样调用doInBackground方法的呢?这就要回想一下,当我们定义好一个AsyncTask的时候,是怎样运行它的呢?代码如下:
private void testMyAsyncTask(){
new TestAsyncTask().execute(url);
}
所以我们只要看看execute方法里面是不是有调用就知道了。
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
} mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params;
exec.execute(mFuture); return this;
}
最终它会调用到上面的方法实现我们复写的三个方法实现。
好了,这篇博客就到这里。理解不够深入、不到的地方,欢迎拍砖!
android开发系列之多线程的更多相关文章
- Android 开发系列教程之(一)Android基础知识
什么是Android Android一词最早是出现在法国作家维里耶德利尔·亚当1986年发表的<未来夏娃>这部科幻小说中,作者利尔·亚当将外表像人类的机器起名为Android,这就是And ...
- [Android开发系列]IT博客应用
1.关于坑 好吧,在此之前先来说一下,之前开的坑,恩,确实是坑,前面开的两个android开发教程的坑,对不起,实在是没什么动力了,不过源码都有的,大家可以参照github这个应用 https://g ...
- Android开发系列之按钮事件的4种写法
经过前两篇blog的铺垫,我们今天热身一下,做个简单的例子. 目录结构还是引用上篇blog的截图. 具体实现代码: public class MainActivity extends Activity ...
- Android开发系列之SQLite
上篇博客提到过SQLite,它是嵌入式数据库,由于其轻巧但功能强大,被广泛的用于嵌入式设备当中.后来在智能手机.平板流行之后,它作为文件型数据库,几乎成为了智能设备单机数据库的必选,可以随着安卓app ...
- Android开发系列之Android项目的目录结构
今天开始正式学习Android开发的种种细节,首先从最基本的概念和操作学起. 首先看一下Android项目的目录结构. 这是我随便建立的一个test项目,我们重点关注一下几个方面的内容: 1.src目 ...
- Android开发系列之学习路线图
通过前面的3篇博客已经简单的介绍了Android开发的过程并写了一个简单的demo,了解了Android开发的环境以及一些背景知识. 接下来这篇博客不打算继续学习Android开发的细节,先停一下,明 ...
- Android开发系列之搭建开发环境
接触Android好久了,记得09年刚在中国大陆有点苗头的时候,我就知道了google有个Android,它是智能机操作系统.后来在Android出1.5版本之后,我第一时间下载了eclipse开发工 ...
- VS2015下的Android开发系列01——开发环境配置及注意事项
概述 VS自2015把Xamarin集成进去后搞Android开发就爽了,不过这安装VS2015完成的时候却是长了不知道多少.废话少说进正题,VS2015安装时注意把Android相关的组件勾选安装, ...
- Android开发系列----sdk下载 环境准备
今天开始准备Android开发环境,FQ下载Android Studio,官网下载地址 https://developer.android.com/studio/install.html (突然发现我 ...
随机推荐
- APP发布Xcode7
一.准备工作 1>准备3.5寸.4寸.4.7寸.5.5寸的程序截图至少个1张,如果支持iPad,那么iPad截图也要有.这些截图尽量截取页面漂亮的,因为这些截图是要放在AppStore中展示的. ...
- No.002 Add Two Numbers
Add Two Numbers Total Accepted: 160702 Total Submissions: 664770 Difficulty: Medium You are given tw ...
- codevs 1049 棋盘染色
题目描述 Description 有一个5×5的棋盘,上面有一些格子被染成了黑色,其他的格子都是白色,你的任务的对棋盘一些格子进行染色,使得所有的黑色格子能连成一块,并且你染色的格子数目要最少.读入一 ...
- [drp 5] pageModel的建立,实现分页查询
导读:之前做的分页,一直都是用的easy--UI分页,然后没有系统的整理过,就是知道传几个参数,然后云云.这次,从头到尾总结一下,了了我的这桩心愿.人事系统的重定向工作,一直刺激着我一定要总结总结这个 ...
- 2016-03-10:libx265源码解析
单步跟踪执行流程 将cli设定为启动项目,在属性->调试->命令行参数中设置如下参数: --input E:\video\pedestrian_area.yuv --fps 24 --in ...
- 开源项目:DolphinPlayer
Dolphin Player是一款基于FFmpeg解码视频播放器,支持大多数的音频和视频格式. 项目主页:http://code.google.com/p/dolphin-player/ 源代码Git ...
- TCP/IP之大明王朝邮差
一位大神的精华之作,原创2016-05-12 刘欣 来自码农翻身! 时间: 大明王朝天启四年, 清晨. 天色刚蒙蒙亮,我就赶着装满货物的马车来到了南城门,这里是集中处理货物的地方,一队一队的马车都来到 ...
- global.autoindex dede:global.itemindex 获取子栏目自动排序序号
{dede:channel row='6' typeid=7 } [field:global.autoindex runphp='yes'] ...
- Android IOS WebRTC 音视频开发总结(六三)-- 2016国内IM云服务行业分析
本文主要国内IM云服务行业分析,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号blackerteam,更多详见www.blackerteam.com 谈到IM我们最先想到的是qq和微信 ...
- YUM软件管理
YUM是一个RPM的前端程序,主要目的是设计用来解决RPM的依赖关系的问题,而不用手动安装所依赖的所有软件.它使用仓库保存管理RPM软件包,仓库的配置文件保存在/etc/yum.repos.d/目录下 ...