android 进程/线程管理(二)----关于线程的迷思
一:进程和线程的由来
进程是计算机科技发展的过程的产物。
最早计算机发明出来,是为了解决数学计算而发明的。每解决一个问题,就要打纸带,也就是打点。
后来人们发现可以批量的设置命令,由计算机读取这些命令,并挨个执行。
在使用的过程中,有一个问题,如果要做I/O操作,是非常耗时的,这个时候CPU是闲着的,这对于计算机资源是一个巨大的浪费。
于是,人们发明了进程这个东西。每个程序就是一个进程,由操作系统管理,当进行复杂的耗时操作是,CPU可以调度处理其他的进程,从而是性能在整体上提高。
线程的目的:
当CPU调度的某个进程时,该进程正在做网络操作,这个时候,如果用户点击某个按钮,是无法及时响应的,体验非常不好。于是,比进程更“小”的调度单位出现了----线程。
我把与用于响应的操作放在一个线程,把耗时的操作放在其他线程,这个用户可以看到界面快速的响应,没有延时的效果。
这也是anroid等现在主流操作系统的编程范式:
把UI操作绑定的主线程,由工作线程处理其他的任务。主要是耗时的任务。
二:线程的启动过程
创建和启动一个新线程,无论经过多少层的封装,最终的目的就是由操作系统提供的api来完成。
以下就是从java thread出发,层层分析,一直到linux的pthread结束。
Thread源码位于:
libcore\libdvm\src\main\java\java\lang\Thread.java
public class Thread implements Runnable {
可见thread是实现了一个runnable接口。

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

runnable什么也没有,就是run函数。
所以线程的根本就是 创建一个新的线程,运行run方法。
下面我们看看创建线程的过程。

public Thread() {
create(null, null, null, 0);
}
public Thread(Runnable runnable) {
create(null, runnable, null, 0);
}

如上所示,常见的应用中使用thread的方法就是
a.定义一个新thread子类,实现run方法。
b.直接传递run给thread作为参数。
接下去我们看下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);
}
public static Thread currentThread() {
return VMThread.currentThread();
}
VmThread源码位于:
VMThread 的 currentThread 是一个 native 方法,其 JNI 实现为 android/dalvik/vm/native/java_lang_VMThread.cpp 中
static void Dalvik_java_lang_VMThread_currentThread(const u4* args,
JValue* pResult)
{
UNUSED_PARAMETER(args); RETURN_PTR(dvmThreadSelf()->threadObj);
}
这里有个dvmThreadSelf()方法:
Thread* dvmThreadSelf()
{
return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
}
可见这是一个存放在key为pthreadKeySelf的索引。
/* the java/lang/Thread that we are associated with */
Object* threadObj;
threadObj关联的就是android thread对象。
接着分析上面的代码,如果没有给新线程指定 group 那么就会指定 group 为当前线程所在的 group 中,然后给新线程设置 name,priority 等。最后通过调用 ThreadGroup 的 addThread 方法将新线程添加到 group 中:
/**
* Called by the Thread constructor.
*/
final void addThread(Thread thread) throws IllegalThreadStateException {
synchronized (threadRefs) {
if (isDestroyed) {
throw new IllegalThreadStateException();
}
threadRefs.add(new WeakReference<Thread>(thread));
}
}
threadRefs里面就是存放group对每一个thread的引用。
通过以上代码分析,thread构造方法仅仅只是设置了一些线程属性,并没有创建真正的线程。
Thread新创建的线程:
public synchronized void start() {
checkNotStarted();
hasBeenStarted = true;
VMThread.create(this, stackSize);
}
Android Thread 的 start 方法很简单,仅仅是转调 VMThread 的 native 方法 create,其 JNI 实现为 android/dalvik/vm/native/java_lang_VMThread.cpp 中的 Dalvik_java_lang_VMThread_create 方法:
static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
{
Object* threadObj = (Object*) args[0];
s8 stackSize = GET_ARG_LONG(args, 1); /* copying collector will pin threadObj for us since it was an argument */
dvmCreateInterpThread(threadObj, (int) stackSize);
RETURN_VOID();
}
dvmCreateInterpThread函数很长,但是它做了最重要的一件事:
bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
{
Thread* self = dvmThreadSelf();

Thread* newThread = allocThread(stackSize);
newThread->threadObj = threadObj;

Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);

pthread_t threadHandle;
int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread); /*
* Tell the new thread to start.
*
* We must hold the thread list lock before messing with another thread.
* In the general case we would also need to verify that newThread was
* still in the thread list, but in our case the thread has not started
* executing user code and therefore has not had a chance to exit.
*
* We move it to VMWAIT, and it then shifts itself to RUNNING, which
* comes with a suspend-pending check.
*/
dvmLockThreadList(self); assert(newThread->status == THREAD_STARTING);
newThread->status = THREAD_VMWAIT;
pthread_cond_broadcast(&gDvm.threadStartCond); dvmUnlockThreadList();

} /*
* Alloc and initialize a Thread struct.
*
* Does not create any objects, just stuff on the system (malloc) heap.
*/
static Thread* allocThread(int interpStackSize)
{
Thread* thread;
thread = (Thread*) calloc(1, sizeof(Thread));

thread->status = THREAD_INITIALIZING;
}
首先通过allocThread创建一个newThread的dalvik thread,并创建了一些属性。将设置其成员变量threadobj传入Android Thread threadobj.
创建vmThreadObj名字的Vmthread对象。
dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
把vmThreadObj的VM_data方法设置成newThread。
然后设置Android Thread vmThread变量为vmThreadObj。
这样通过vmThreadObj, Android Thread就和dalvik thread关联起来了。
然后就是
int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread);
Yes,这个就是linux操作系统创建新线程的API接口!
接下来我们分析interpThreadStart,这是运行在新线程的入口。
/*
* pthread entry function for threads started from interpreted code.
*/
static void* interpThreadStart(void* arg)
{
Thread* self = (Thread*) arg; std::string threadName(dvmGetThreadName(self));
setThreadName(threadName.c_str()); /*
* Finish initializing the Thread struct.
*/
dvmLockThreadList(self);
prepareThread(self); while (self->status != THREAD_VMWAIT)
pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock); dvmUnlockThreadList(); /*
* Add a JNI context.
*/
self->jniEnv = dvmCreateJNIEnv(self); /*
* Change our state so the GC will wait for us from now on. If a GC is
* in progress this call will suspend us.
*/
dvmChangeStatus(self, THREAD_RUNNING); /*
* Execute the "run" method.
*
* At this point our stack is empty, so somebody who comes looking for
* stack traces right now won't have much to look at. This is normal.
*/
Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
JValue unused; ALOGV("threadid=%d: calling run()", self->threadId);
assert(strcmp(run->name, "run") == 0);
dvmCallMethod(self, run, self->threadObj, &unused);
ALOGV("threadid=%d: exiting", self->threadId); /*
* Remove the thread from various lists, report its death, and free
* its resources.
*/
dvmDetachCurrentThread(); return NULL;
} /*
* Finish initialization of a Thread struct.
*
* This must be called while executing in the new thread, but before the
* thread is added to the thread list.
*
* NOTE: The threadListLock must be held by the caller (needed for
* assignThreadId()).
*/
static bool prepareThread(Thread* thread)
{
assignThreadId(thread);
thread->handle = pthread_self();
thread->systemTid = dvmGetSysThreadId(); setThreadSelf(thread); return true;
} /*
* Explore our sense of self. Stuffs the thread pointer into TLS.
*/
static void setThreadSelf(Thread* thread)
{
int cc; cc = pthread_setspecific(gDvm.pthreadKeySelf, thread); }
首先从Android Thread获得name,然后通过prepareThread设置线程的一些属性。并调用setThreadSelf方法,把dalvik thread放入TLS。
然后执行Android Thread的run方法。
public void run() {
if (target != null) {
target.run();
}
}
至此,通过操作系统提供的接口,thread里面的run方法,在新线程中运行起来了!
本文参考:
1.《深入理解android内核设计思想》林学森
2.《Android内核剖析》
3.罗朝辉 http://www.cppblog.com/kesalin/archive/2014/07/11/android_thread_impl.html
相关文章:
android 进程/线程管理(三)----Thread,Looper / HandlerThread / IntentService
android 进程/线程管理(二)----关于线程的迷思的更多相关文章
- Android线程管理之ExecutorService线程池
前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...
- C#中的线程(二)线程同步
C#中的线程(二)线程同步 Keywords:C# 线程Source:http://www.albahari.com/threading/Author: Joe AlbahariTranslato ...
- pThreads线程(二) 线程同步--互斥量/锁
互斥量(Mutex)是“mutual exclusion”的缩写.互斥量是实现线程同步,和保护同时写共享数据的主要方法. 互斥量对共享数据的保护就像一把锁.在Pthreads中,任何时候仅有一个线程可 ...
- Java内存模型与线程(二)线程的实现和线程的调度
先行先发生原则(happen-before原则) 先行先发生是指Java内存模型中定义的两项操作之间的偏序关系. 如果说A先行于B,其实就是说在发生B操作之前,操作A产生的影响能被操作B观察到,至于这 ...
- java 线程 (二) 线程池
package cn.sasa.demo2; import java.util.concurrent.ExecutorService; import java.util.concurrent.Exec ...
- Java线程池二:线程池原理
最近精读Netty源码,读到NioEventLoop部分的时候,发现对Java线程&线程池有些概念还有困惑, 所以深入总结一下 Java线程池一:线程基础 为什么需要使用线程池 Java线程映 ...
- C#中的线程(二)线程同步基础 (读后感)
参考文章:https://www.cnblogs.com/dingfangbo/p/5769501.html 一.lock 确保只有一个线程访问某个资源或某段代码.通俗的讲就是多个线程操作相同的锁对象 ...
- QT线程(二)---线程同步
线程互斥 多线程运行时,通常会访问同一个变量,同一个数据结构,或者同一段代码.因此,需要使用互斥技术来保护上述资源,确保多线程执行的正确性. 注: 我们通常说某个函数是线程安全的,也就是因为该函数 ...
- java多线程与线程并发二:线程互斥
本文章内容整理自:张孝祥_Java多线程与并发库高级应用视频教程 当两条线程访问同一个资源时,可能会出现安全隐患.以打印字符串为例,先看下面的代码: // public class Test2 { p ...
- Android线程管理之Thread使用总结
前言 最近在一直准备总结一下Android上的线程管理,今天先来总结一下Thread使用. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Executo ...
随机推荐
- NPM install - killed error solution
在接手一个Node项目的时候,npm install.却出现了"killed"的错误.以为是Node版本的问题,熟练地切换了0.11与0.10版,同样无解. 由于新的npm版本吧, ...
- 利用PS自动切图、支持svg且支持icoMoon——再也不用四处去转格式了
今天想导出svg格式的图片支持webFont,结果AI打不开了,文件好像损坏了,于是就想办法在PS里面导出. 网上搜索到一篇文章,腾讯的 http://isux.tencent.com/ps-phot ...
- 【Java】一次SpringMVC+ Mybatis 配置多数据源经历
需求 现在在维护的是学校的一款信息服务APP的后台,最近要开发一些新功能,其中一个就是加入学校电影院的在线购票.在线购票实际上已经有一套系统了,但是是外包给别人开发的,我们拿不到代码只能拿到数据库,并 ...
- 初遇sql server
今天初始接触sql server 和mysql的语法有一些不同 sql server中使用[] 或双引号来表示数据库.字段名.表名等,而字符串使用单引号来表示 mysql中数据库名,表名,字段名不需要 ...
- Windows及Linux平台下的计时函数总结
本文对Windows及Linux平台下常用的计时函数进行总结,包括精度为秒.毫秒.微秒三种精度的各种函数.比如Window平台下特有的Windows API函数GetTickCount().timeG ...
- EFcodeFirst+T4=操纵任意数据库
之前有写过两篇,EF选择Mysql数据源 跟 EF添加ADO.NET实体模型处直接选择Oracle数据源,其方便之处就不多说了,使用DBfirst直接点点点就能与数据库双向更新,而且关键是方便我们使用 ...
- Week1项目报告
1. 预测时间 Personal Software Process Stages Time(h) 计划 · 估计这个任务需要多少时间 16.5 开发 · 需求分析 (包括学习新技术) 4 · 生成设计 ...
- C#入门经典第五版之变量的更多内容编码题训练
1. 编写一个控制台应用程序,它接收用户输入的一个字符串,将其中的字符以与输入相反的顺序输出. public string ReverseString(string str) { string rev ...
- 【分享】深入浅出WPF全系列教程及源代码
来源:http://blog.csdn.net/yishuaijun/article/details/21345893 本来想一篇一篇复制的.但是考虑到别人已经做过了,就没有必要了吧,就给大家一个目录 ...
- 与众不同 windows phone 8.0 & 8.1 系列文章索引
[源码下载] [与众不同 windows phone 7.5 (sdk 7.1) 系列文章索引] 与众不同 windows phone 8.0 & 8.1 系列文章索引 作者:webabcd ...