Thread和ThreadGroup分析
Thread类分析
用Thread的时候一直就直接传递一个runnable或者futuretask,还从没有注意到Thread类还有几个别的构造函数
1. 构造函数,一切的起源
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
init方法分析
init方法入参是所有构造方法调用的方法,在调用的时候acc
和inheritThreadLocals
(是否要继承ThreadLocal)都是Null。
假设当前要创建的线程是A,当前线程是B。
在这个方法里面会设置线程属性,会将B线程变为A线程的父类。如果A线程乜有ThreadGroup。就会添加到B线程的ThreadGroup里面,并且使ThreadGroup中的未启动的线程的数量++
为一些属性设置默认值。如果需要继承B线程的ThreadLocal,就会利用B线程中的ThreadLocal创建一个ThreadLocalMap,并且赋值给A线程的inheritableThreadLocals属性。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
// 拿到当前的线程,作为要创建线程的父线程
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
// 如果没有指定ThreadGroup
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
// 如果上面的security还是没有,就把这个线程和当前线程放在一个group中。
if (g == null) {
g = parent.getThreadGroup();
}
}
//
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
// 给group中 未启动的线程树++
g.addUnstarted();
//一波赋值操作,用过线程的都知道,这几个属性都是可以通过set方法修改的
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
// 赋值target方法
this.target = target;
setPriority(priority);
// 当前线程inheritThreadLocals不为null并且父线程inheritableThreadLocals不为null
// 如果要继承ThreadLocal。就会给当前线程创建一个threadLocal。并且赋值给inheritThreadLocals。
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
2. 属性分析
private volatile String name; // 名字
private int priority; // 优先级
private Thread threadQ; //
private long eetop;
/* Whether or not to single_step this thread. */
// 判断当前线程是否是single_step(单步执行)
private boolean single_step;
/* Whether or not the thread is a daemon thread. */
// 是否是守护线程,默认是false
private boolean daemon = false;
// jvm的状态
private boolean stillborn = false;
//需要运行的Runnable。
private Runnable target;
// 这个线程所在的线程组。
private ThreadGroup group;
/* The context ClassLoader for this thread */
private ClassLoader contextClassLoader;
/* The inherited AccessControlContext of this thread */
private AccessControlContext inheritedAccessControlContext;
/* For autonumbering anonymous threads. */
// 给匿名的线程的自动编号
private static int threadInitNumber;
// 和这个线程有关的ThreadLocal的值,这个map保留着ThreadLocal
ThreadLocal.ThreadLocalMap threadLocals = null;
// 内部的threadLocal
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
// 栈的大小,如果为0的话,JVM机会忽略。
private long stackSize;
/*
* JVM-private state that persists after native thread termination.
*/
private long nativeParkEventPointer;
/*
* Thread ID
*/
private long tid;
/* For generating thread ID */
private static long threadSeqNumber;
// 线程状态,初始化为0,表示没有运行
private volatile int threadStatus = 0;
// 提供给LockSupport的park方法用的
volatile Object parkBlocker;
/* The object in which this thread is blocked in an interruptible I/O
* operation, if any. The blocker's interrupt method should be invoked
* after setting this thread's interrupt status.
*/
// 此线程在可中断 I/O 操作中被阻塞的对象,如果设置,就会设置此线程的中断状态后应调用阻塞程序的中断方法
private volatile Interruptible blocker;
private final Object blockerLock = new Object();
// 最低优先级
public final static int MIN_PRIORITY = 1;
// 默认的优先级
public final static int NORM_PRIORITY = 5;
// 最大
public final static int MAX_PRIORITY = 10;
// 栈帧对象。一个StackTraceElement表示一个栈帧
private static final StackTraceElement[] EMPTY_STACK_TRACE
= new StackTraceElement[0];
// 权限检查
private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION =
new RuntimePermission("enableContextClassLoaderOverride");
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// 线程异常处理,这是JVM自己调用的
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
long threadLocalRandomSeed;
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
3. start方法分析
在start方法里面,简单来说,就是启动线程,将当前线程添加到group中。
public synchronized void start() {
// 线程开始的状态必须是0
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 将当前线程添加到所在的组里面,与此同时,这个组里面未启动的线程数--,启动的数++
group.add(this);
//
boolean started = false;
try {
// 这是真正的启动一个线程
// 但是这个方法是native方法
start0();
started = true;
} finally {
try {
//从group中移除这个线程
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 这里异常什么都不要做,如果 start0 真的异常的话,直接从JVM层面就开始报错了。
// 异常栈出来了。
}
}
}
4. interrupt方法分析
中断当前线程,中断对于线程来说就是一个标志位置。
在等待状态的线程,中断的时候会清除中断标志,并且会抛出InterruptedException
如果此线程在 InterruptibleChannel 上的 I/O 操作中被阻塞,则channel将关闭,就会设置线程的中断状态,并且线程将收到 java.nio.channels.ClosedByInterruptException。
如果此线程在 java.nio.channels.Selector 中被阻塞,就会设置线程的中断状态将被设置,当前线程就会从select方法中返回,可能就没有结果而已。
对于blocking或者runnable的线程,中断也只是一个标志位置,那么,对于blocking状态的线程,他是不响应中断的。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
// 如果这个线程有一个可以中断的io操作,在设置完当前线程的中断标志之后,就会尝试去中断他
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
5. setPriority
优先级的必须是在10和1之间,设置的优先级不能大于线程所在组的最大优先级。
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
6. setName分析
看这个方法,好像能在线程启动之后还能修改线程的名字。好像很有意思。比如像下面的一样
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
动态修改线程名字
一开始是t,运行之后修改为b
7. 线程运行时候的几个状态
public enum State {
// 新创建但是还没有7运行
NEW,
// 这个状态,表示线程可能在,也可能不(就绪,等待时间片的调度)在运行。
RUNNABLE,
// 获取锁失败
BLOCKED,
// 等待状态
WAITING,
// 带超时时间的等待
TIMED_WAITING,
// 结束,表示线程执行完成
TERMINATED;
}
8. 对于 UncaughtExceptionHandler属性的说明,(重点)
这属性,肯定有对应的set方法, 它是Thread类中的一个内部类
这个接口是提供给JVM调用的,会处理线程没有处理的异常。在线程发送未捕获的异常马上要终止的时候,JVM会调用getUncaughtExceptionHandler
方法查询对应的方法, 将异常线程和异常作为参数传递,如果没有设置,就会将ThreadGroup
作为
UncaughtExceptionHandler
,本身,ThreadGroup就是实现了UncaughtExceptionHandler接口,如果ThreadGroup也没有,就给默认的处理。
@FunctionalInterface
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
那么看看ThreadGroup中是怎么写的.
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
看这个一下就懂了,线程异常了之后,的提示文本是从哪里来的
9. Join方法分析
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 为0表示一直等待
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
//避免重复唤醒。
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
// 一直等待,
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
要注意到,join方法的锁,表示当前线程要在join的thread上面等待,那就是说,A,B两个线程,在B线程里面执行A.join,会让B线程等待在A线程的Thread对象所关联的锁上面。
在这个线程终止的时候,就会调用this.notifyAll
方法,唤醒之前等待的B线程,B就可以继续执行了。
线程类就分析到这里了,分析了一些方法,主要是UncaughtExceptionHandler和可以动态的设置线程的名字让我很惊艳,还有ThreadGroup的相关内容。下面就分析分析ThreadGroup。
ThreadGroup分析
见名知意。这是用来管理一组线程的。将线程放在一个组里面,便于管理。
一个线程组代表一组线程。 一个线程组还可以包含其他线程组。 除了初始线程组之外的每个线程都有一个父线程组,这种关系可以组成一个树。一个节点下面可以有多个子节点,但是只能有一个父节点。一个线程可以访问关于它自己的线程组的信息,但不能访问关于它的线程组的父线程组或任何其他线程组的信息。
下面就详细的看看他的属性和方法
1. 属性分析
//线程组
private final ThreadGroup parent;
String name;
int maxPriority;
boolean destroyed;
boolean daemon;
boolean vmAllowSuspension;
// 没有启动的线程
int nUnstartedThreads = 0;
// 启动的线程
int nthreads;
// 当前线程组包含的线程
Thread threads[];
//
int ngroups;
// 当前组下面的组
ThreadGroup groups[];
2. 构造方法
还是简单的设置一些属性而已
private ThreadGroup(Void unused, ThreadGroup parent, String name) {
this.name = name;
this.maxPriority = parent.maxPriority;
this.daemon = parent.daemon;
this.vmAllowSuspension = parent.vmAllowSuspension;
this.parent = parent;
parent.add(this);
}
在构造Thread的时候会将ThreadGroup传递进去。在start方法的时候会将线程添加进来
3. add方法分析
可以看到,一开始的时候就创建了长度为4的线程,并且扩容就是*2,给下标赋值,nUnstartedThreads–(nUnstartedThreads表示当前没启动的线程数)
当然这里还有对应的remove方法,这里就不写了,就是将线程移出数组。
void add(Thread t) {
synchronized (this) {
if (destroyed) {
throw new IllegalThreadStateException();
}
if (threads == null) {
threads = new Thread[4];
} else if (nthreads == threads.length) {
threads = Arrays.copyOf(threads, nthreads * 2);
}
threads[nthreads] = t;
// This is done last so it doesn't matter in case the
// thread is killed
nthreads++;
// The thread is now a fully fledged member of the group, even
// though it may, or may not, have been started yet. It will prevent
// the group from being destroyed so the unstarted Threads count is
// decremented.
nUnstartedThreads--;
}
}
4. activeCount分析
这只是一个快照的方式,所以,就不需要持有锁。
会统计当前组下面所有的组中总共的线程数。
public int activeCount() {
int result;
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
// 如果当前的group已经销毁掉了,直接返回0
if (destroyed) {
return 0;
}
// 当前运行的线程数量
result = nthreads;
// 当前线程所拥有的group数量
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
// 统计当前节点下面所有组的线程数
result += groupsSnapshot[i].activeCount();
}
return result;
}
5. interrupt分析
简单的说,就会遍历整个下面所有的线程,然后调用他的中断方法,既然是ThreadGroup了,肯定里面存放的线程都是一类的。
public final void interrupt() {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
checkAccess();
for (int i = 0 ; i < nthreads ; i++) {
threads[i].interrupt();
}
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
groupsSnapshot[i].interrupt();
}
}
关于Thread和ThreadGroup分析就分析到这里了。 如有不正确的地方,欢迎指出。谢谢。