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;
    }

最终它会调用到上面的方法实现我们复写的三个方法实现。

好了,这篇博客就到这里。理解不够深入、不到的地方,欢迎拍砖!

时间: 2024-08-24 21:00:52

android开发系列之多线程的相关文章

C#程序员学习Android开发系列之ListView

上篇博客解决了Android客户端通过WebService与服务器端程序进行交互的问题,这篇博客重点关注两个问题,一个是Android应用程序如何与本机文件型数据库SQLite进行交互,另一问题则是如何在ListView中按照我们想要的界面效果进行展示.限于篇幅这篇重点讲ListView,下篇博客重点阐述SQLite. ListView是一个常用的数据显示控件,假设我们要做一个简单的界面,如图所示. 这张图是我直接从Android平板电脑(Android 4.2.2)上面截图下来的,就是一个普通

C#程序员学习Android开发系列之学习路线图

通过前面的3篇博客已经简单的介绍了Android开发的过程并写了一个简单的demo,了解了Android开发的环境以及一些背景知识. 接下来这篇博客不打算继续学习Android开发的细节,先停一下,明确一下接下来的学习目标以及学习路线. 一.对Android开发的基本认识 1.Android原生开发是基于Java语言的,由于我比较擅长C#,所以对Java语言本身不太熟练,需要加强Java语言基础的练习,这一块我会穿插到具体的知识点练习当中,并且在必要的地方给出与C#语言的对比(其实基本上在语法层

快速Android开发系列网络篇之Android-Async-Http

快速Android开发系列网络篇之Android-Async-Http 转:http://www.cnblogs.com/angeldevil/p/3729808.html 先来看一下最基本的用法 AsyncHttpClient client = new AsyncHttpClient(); client.get("http://www.google.com", new AsyncHttpResponseHandler() { @Override public void onSucce

快速Android开发系列网络篇之Retrofit

Retrofit是一个不错的网络请求库,用官方自己的介绍就是: A type-safe REST client for Android and Java 看官网的介绍用起来很省事,不过如果不了解它是怎么实现的也不太敢用,不然出问题了就不知道怎么办了.这几天比较闲就下下来看了一下,了解一下大概实现方法,细节就不追究了.先来看一个官网的例子,详细说明去网官看 简单示例 首先定义请求接口,即程序中都需要什么请求操作 public interface GitHubService { @GET("/use

C#程序员学习Android开发系列之SQLite

上篇博客提到过SQLite,它是嵌入式数据库,由于其轻巧但功能强大,被广泛的用于嵌入式设备当中.后来在智能手机.平板流行之后,它作为文件型数据库,几乎成为了智能设备单机数据库的必选,可以随着安卓app打包到apk文件当中. SQLite的官方网站是http://www.sqlite.org/,可以任意下载,上面也有详尽的文档可以参考,这篇博客重点关注SQLite在Android开发中如何使用. 在Android开发中,推荐建立一个类继承自SQLiteOpenHelper来创建数据库操作类,比如:

快速Android开发系列网络篇之Volley

Volley是Google推出的一个网络请求库,已经被放到了Android源码中,地址在这里,先看使用方法 RequestQueue mRequestQueue = Volley.newRequestQueue(context); JsonObjectRequest req = new JsonObjectRequest(URL, null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONO

C#程序员学习Android开发系列之Android项目的目录结构

今天开始正式学习Android开发的种种细节,首先从最基本的概念和操作学起. 首先看一下Android项目的目录结构. 这是我随便建立的一个test项目,我们重点关注一下几个方面的内容: 1.src目录:存放java源代码的目录,里面建立一个包,包里面有4个java源文件(分别都继承自Activity).由于java要求比较严格,因此要求类名与文件名一致. gen(Generated Java Files)目录:自动产生Java源文件的目录,是由工具自动生成的,一般不需要自己修改.里面主要有一个

C#程序员学习Android开发系列之搭建开发环境

接触Android好久了,记得09年刚在中国大陆有点苗头的时候,我就知道了google有个Android,它是智能机操作系统.后来在Android出1.5版本之后,我第一时间下载了eclipse开发工具.adt以及android sdk,体验了一把android开发,记得当时搭建开发环境相当麻烦.由于android开发是基于Java的,所以除了上述的工具之外,首先需要安装jdk,记得当时流行的是jdk1.5(后来是1.6,现在是1.7,未来是1.8),安装好之后需要设置环境变量. 时隔4年之后,

C#程序员学习Android开发系列之调用WebService

我在学习Android开发过程中遇到的第一个疑问就是Android客户端是怎么跟服务器数据库进行交互的呢?这个问题是我当初初次接触Android时所困扰我的一个很大的问题,直到几年前的一天,我突然想到WebService是否可以呢?让WebService充当服务器端的角色,完成与服务器数据库操作相关的事情,而Android客户端只要按照WebService方法参数的要求去调用就行了.在当时我对这个解决方案的实现还没模糊,我想这个问题也是初学Android的朋友肯定会想到的问题.那么现在就让我们动