今天在这篇博客里面,我只想谈谈自己对程序开发里面避无可避的一个问题-多线程的一些看法与思考。

其实说到多线程这个名称相信只要接触过软件这个行业的人都已经耳熟能详了,但是如果被问到到底什么才是多线程呢?为什么我们会需要多线程呢?多线程又会造成什么副作用呢?其实这些都应该是值得我们深入思考的一些问题,因为在多线程的环境里面,往往会发生一些意想不到的事情。下面让我们来针对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开发系列之多线程的更多相关文章

  1. Android 开发系列教程之(一)Android基础知识

    什么是Android Android一词最早是出现在法国作家维里耶德利尔·亚当1986年发表的<未来夏娃>这部科幻小说中,作者利尔·亚当将外表像人类的机器起名为Android,这就是And ...

  2. [Android开发系列]IT博客应用

    1.关于坑 好吧,在此之前先来说一下,之前开的坑,恩,确实是坑,前面开的两个android开发教程的坑,对不起,实在是没什么动力了,不过源码都有的,大家可以参照github这个应用 https://g ...

  3. Android开发系列之按钮事件的4种写法

    经过前两篇blog的铺垫,我们今天热身一下,做个简单的例子. 目录结构还是引用上篇blog的截图. 具体实现代码: public class MainActivity extends Activity ...

  4. Android开发系列之SQLite

    上篇博客提到过SQLite,它是嵌入式数据库,由于其轻巧但功能强大,被广泛的用于嵌入式设备当中.后来在智能手机.平板流行之后,它作为文件型数据库,几乎成为了智能设备单机数据库的必选,可以随着安卓app ...

  5. Android开发系列之Android项目的目录结构

    今天开始正式学习Android开发的种种细节,首先从最基本的概念和操作学起. 首先看一下Android项目的目录结构. 这是我随便建立的一个test项目,我们重点关注一下几个方面的内容: 1.src目 ...

  6. Android开发系列之学习路线图

    通过前面的3篇博客已经简单的介绍了Android开发的过程并写了一个简单的demo,了解了Android开发的环境以及一些背景知识. 接下来这篇博客不打算继续学习Android开发的细节,先停一下,明 ...

  7. Android开发系列之搭建开发环境

    接触Android好久了,记得09年刚在中国大陆有点苗头的时候,我就知道了google有个Android,它是智能机操作系统.后来在Android出1.5版本之后,我第一时间下载了eclipse开发工 ...

  8. VS2015下的Android开发系列01——开发环境配置及注意事项

    概述 VS自2015把Xamarin集成进去后搞Android开发就爽了,不过这安装VS2015完成的时候却是长了不知道多少.废话少说进正题,VS2015安装时注意把Android相关的组件勾选安装, ...

  9. Android开发系列----sdk下载 环境准备

    今天开始准备Android开发环境,FQ下载Android Studio,官网下载地址 https://developer.android.com/studio/install.html (突然发现我 ...

随机推荐

  1. panguan(判官):一个自研的任务执行引擎的工程实践

    来某厂接近半年了,几乎没写过C++代码,说实话还真的有点手生.最近刚好有一个需求,然而我感觉我也没有办法用C++以外的语言去实现它.于是还是花了几天时间用C++完成编码,这是一个简单的任务执行引擎,它 ...

  2. 慕课网-安卓工程师初养成-1-1 Java简介

    来源 http://www.imooc.com/video/1430 主要内容 Java平台应用 核心概念:JVM,JDK,JRE 搭建Java开发环境 使用工具开发安卓程序 经验技巧分享 Java历 ...

  3. 学习练习 java面向对象封装汽车

    package com.hanqi; //汽车 public class Car { // 车牌 private String CheP; // 油箱容量 private double YouXRL ...

  4. Devexpress中WebChartControl控件柱状统计图的做法(数据为调用存储过程)

    //前台控件代码:WebChartControl控件: <%-- 月采购量统计--%> <dxchartsui:WebChartControl ID="WebChartCo ...

  5. 操作笔记:linux下查看端口被占用

    [root@iZ945sgm0ugZ /]# lsof -i:8080 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 1192 jet ...

  6. 关键字 this 的作用

    1.关键字 this ①是指当前对象自己 当一个类中要明确指出使用对象自己的变量或函数时,就应该加上this关键字,小栗子a如下: public class A { string Name = &qu ...

  7. 互斥对象 Mutex 和MFC中的CMutex

    互斥(Mutex)是一种用途非常广泛的内核对象.能够保证多个线程对同一共享资源的互斥访问.同临界区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共 ...

  8. Unity Js与C#脚本通信

    将.js文件放到Standard Assets目录下,否则无法编译通过 CS_test.cs : using UnityEngine; using System.Collections;   publ ...

  9. SAS 5/iR Adapter 驱动下载

    http://www.dell.com/support/home/cn/zh/cnbsd1/Drivers/DriversDetails?driverId=FF6F6

  10. ios中,长按Webview中的图片

    我们所要解决的问题如题目所示:ios中,长按Webview中的图片,将图片保存到本地相册. 解决方案:对load的html网页,执行js注入,通过在webview中执行js代码,来响应点击事件,通过j ...