概念
线程是并发执行的单位。它内部的参数,局部变量和唤醒的方法都有自己的调用堆。当一个应用打开的时候至少有一个线程在运行,它就是主线程,在主线程组中。运行时在系统线程组中保持它自己的线程。
有两种方式运行一个新线程。你可以实现一个Thread的子类,然后覆写它的run方法,或者可以创建一个Thread,然后将一个Runnable对象当参数传入。无论使用上述哪种方式,都需要使用start方法来运行这个新线程。
每个线程都有一个int型的参数即优先级,它决定了这个线程如何被系统调度。新的线程继承父进程的优先级。一个线程可以使用setPriority(int)方法设定优先级。
源码分析
这次我准备从Thread调用方式入手,通常来说有两种调用方式,上文中也有提到。接下来我煮个��:
class MyThread extends Thread {
@Override
public void run() {
super.run();
......
}
}
MyThread myThread = new MyThread();
myThread.start();Thread thread = new Thread(new Runnable() {
@Override
public void run() {
.....
}
});
thread.start();
第一种就是构造一个Thread的子类然后覆写run方法,第二种是直接将Runnable当参数传入,并覆写Runnable里面的run方法。推荐使用第二种方式,更简洁。
现在进入到构造函数中
public Thread() {
create(null, null, null, 0);
}
public Thread(Runnable runnable) {
create(null, runnable, null, 0);
}
我们会发现构造函数非常多,抽取出两个我们用到频率比较高的,如上��
他们同时进入了一个叫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);
}
四个参数,线程组、runnable、线程名、堆大小。首先获取了当前所在线程currentThread,调用了一些native的东西
public static native Thread currentThread();
获取当前线程所在线程组:
group = currentThread.getThreadGroup();
public final ThreadGroup getThreadGroup() {
// TODO This should actually be done at native termination.
if (getState() == Thread.State.TERMINATED) {
return null;
} else {
return group;
}
}
如果当前线程状态不为终止,那么返回group。
然后设定线程id、线程名称、Runnable、堆大小、优先级等。线程id是诶一识别一个线程的东西,所以每个线程的id都是独一无二的,如果用户没有直接设定线程名称那么默认线程名为 Thread- + id 的格式,最后将线程推入线程组中。
当你创建了一个线程的时候,一定要用start()方法启动它,要不你创建它干嘛~在构造函数部分没发现什么激动人心的东西,于是我们来到了直接启动线程的start()方法。
public synchronized void start() {
checkNotStarted();
hasBeenStarted = true;
nativeCreate(this, stackSize, daemon);
}
想不到这么叼的方法居然只有三行啊哈哈哈��,让我们开始追根溯源
private void checkNotStarted() {
if (hasBeenStarted) {
throw new IllegalThreadStateException("Thread already started");
}
}
checkNotStarted()就是判断是这个线程是不是已经启动,如果你已经调用过一次start了,第234567…次调用start就会抛出线程已启动的异常的哟~
hasBeenStarted = true;
将线程状态置为启动,nativeCreate(this, stackSize, daemon);
调用了一个native方法:private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
,靠。。这就结束了。。。我一定是忘了什么。。。。。
老夫掐指一算在分析构造函数的时候有个方法结束了全篇,this.group.addThread(this);
,对,就是它!将新建的线程扔到线程组就不管了,所以老夫猜测关于thread的调度之类的必在ThreadGroup中!
ThreadGroup构造方法:
/**
* Constructs a new {@code ThreadGroup} with the given name. The new {@code ThreadGroup}
* will be child of the {@code ThreadGroup} to which the calling thread belongs.
*
* @param name the name
* @see Thread#currentThread
*/
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
/**
* Constructs a new {@code ThreadGroup} with the given name, as a child of the
* given {@code ThreadGroup}.
*
* @param parent the parent
* @param name the name
* @throws NullPointerException if {@code parent == null}
* @throws IllegalThreadStateException if {@code parent} has been
* destroyed already
*/
public ThreadGroup(ThreadGroup parent, String name) {
if (parent == null) {
throw new NullPointerException("parent == null");
}
this.name = name;
this.parent = parent;
if (parent != null) {
parent.add(this);
this.setMaxPriority(parent.getMaxPriority());
if (parent.isDaemon()) {
this.setDaemon(true);
}
}
}
/**
* Initialize the special "system" ThreadGroup. Was "main" in Harmony,
* but we have an additional group above that in Android.
*/
private ThreadGroup() {
this.name = "system";
this.parent = null;
}
也就是说如果你自己没有指定父线程组的话,那么就使用系统提供的ThreadGroup,系统已经默默为你创建了一个main thread 的时候就默默为你创建了一个 main ThreadGroup,如果你自己指定了父线程组就直接将线程添加到指定的线程组啦。
直接进入addThread方法:
final void addThread(Thread thread) throws IllegalThreadStateException {
synchronized (threadRefs) {
if (isDestroyed) {
throw new IllegalThreadStateException();
}
threadRefs.add(new WeakReference<Thread>(thread));
}
}
threadRefs是保存了线程组中包含的所有线程的弱引用,使用弱引用是为了防止内存溢出的,内存中只存在弱引用的对象是可以被回收的,不会造成内存泄露。但是又是谁调度的ThreadGroup的呢!
好像不太对!在哪调度的!!我想了又想猜了又猜!!突然灵光一现!!Looper!!这个专业负责调度线程的东西我怎么给忘了!!
在Looper的prepare方法中:
public static void prepare() {
prepare(true);
}
private static void prepare(booleanquitAllowed) {
if (sThreadLocal.get() != null) {
throw newRuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(newLooper(quitAllowed)); //新建一个looper对象,并保存到线程本地变量中
/// M: ALPS00297986
long instances =VMDebug.countInstancesOfClass(Looper.class, false);
// check if the looper instance over alimit, it should has some leakage.
if(100 < instances)
{
Log.e(TAG,"WARNING: The Looperclass instance count has over a limit(100). There should be some leakage ofLooper or HandlerThread.");
Log.e(TAG,"Looper classinstance count = " + instances);
Log.e(TAG,"Current ThreadName: " + Thread.currentThread().getName());
Thread.currentThread().getThreadGroup().list();
Thread.currentThread().dumpStack();
}//if
/// M: ALPS00297986
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //新建一个消息队列
mThread = Thread.currentThread(); //保存当前线程
}
看到这一行没!!Thread.currentThread().getThreadGroup().list();
哈哈哈,就是在Looper里面被调度的,哈哈哈,但是我悲伤地发现这个方法已经被更新了。。。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
欲哭无泪,我忽然想到一个Looper对应一个Thread,所以Thread之间的调度应该不是Looper负责的。。~~(>_<)~~ ,我只好淡淡的滚回ThreadGroup里面继续寻找。。。
在ThreadGroup中所有的Thread的弱引用都被存到了一个List中,我试图找到调用了这个List的get方法的地方,也就是Thread被取出的地方,于是我找到了这里
/**
* Copies into <param>enumeration</param> starting at
* <param>enumerationIndex</param> all Threads or ThreadGroups in the
* receiver. If <param>recurse</param> is true, recursively enumerate the
* elements in subgroups.
*
* If the array passed as parameter is too small no exception is thrown -
* the extra elements are simply not copied.
*
* @param enumeration array into which the elements will be copied
* @param recurse Indicates whether subgroups should be enumerated or not
* @param enumerationIndex Indicates in which position of the enumeration
* array we are
* @param enumeratingThreads Indicates whether we are enumerating Threads or
* ThreadGroups
* @return How many elements were enumerated/copied over
*/
private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex,
boolean enumeratingThreads) {
if (enumeratingThreads) {
synchronized (threadRefs) {
// walk the references directly so we can iterate in reverse order
for (int i = threadRefs.size() - 1; i >= 0; --i) {
Thread thread = threadRefs.get(i).get();
if (thread != null && thread.isAlive()) {
if (enumerationIndex >= enumeration.length) {
return enumerationIndex;
}
enumeration[enumerationIndex++] = thread;
}
}
}
} else {
synchronized (groups) {
for (int i = groups.size() - 1; i >= 0; --i) {
if (enumerationIndex >= enumeration.length) {
return enumerationIndex;
}
enumeration[enumerationIndex++] = groups.get(i);
}
}
}
if (recurse) {
synchronized (groups) {
for (ThreadGroup group : groups) {
if (enumerationIndex >= enumeration.length) {
return enumerationIndex;
}
enumerationIndex = group.enumerateGeneric(enumeration, recurse,
enumerationIndex, enumeratingThreads);
}
}
}
return enumerationIndex;
}
看完翻译有些蒙圈,大概是把这些thread们全都拷贝了,可能是拷贝到了native层啥的之类的。。
突然我想起了度娘,于是搜了下“thread调度”惊讶的发现尼玛有个专门的调度器哟!!劳资居然妄想在代码里找到Thread是怎么调度的,还一步步坑进ThreadGroup和Looper!!简直气死宝宝!!