Java源码分析九(Thread)
Java源码分析九(Thread)
本文在文字解读方面借鉴了很多博客
多线程编程一直是业界公认比较难也是比较重要,
而且是非常基础的一点,掌握它非常重要。Java中多线程编程比较幸福,
因为Jdk工程师们考虑了很多东西尽量减少使用Java的难度和复杂度。
其实在C++之中是没有内建多线程的,它依赖操作系统提供这个特性,因为C++为了效率,
控制C++适用的应用程序的范围。即C++没有内建多线程,它允许你直接使用操作系统提供的多线程。
这也意味着你在Linux系统下编写的C++多线程代码很有可能在Windows下不能运行,
这也是架构师技术选型的时候一个重要考虑点。
什么线程
线程就是操作系统的最小运行单位,在线程之上还有进程。Java允许有多线程。
Java之中线程有优先级priority,子线程优先级默认和父线程一样。
Java中线程的周期**
多线程的声明周期概念:
Java之中线程的实现
在Java之中线程控制和业务分离,而且线程的实现有两种方式:
守护线程和非守护线程
守护(daemon)线程:服务其他的线程,比如垃圾回收线程,就是最典型的守护线程。它的最主要特征是:**它守护的线程挂了,它就会停止执行,即使它的实现里面有finally块。即daemon线程的finally块不一定执行** 非守护(daemon)线程:用户线程,它的特征是:除非主动终止,否则任务会一直执行,直到terminated触发。 User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了。 **即user thread会在自己的任务执行完之后才会退出,即使父线程退出了。** **thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常**Thread的一些基本属性
- name 线程的名称。每个线程都可以有name,java允许线程拥有相同的名字,不指定thread name,就会给你generate 一个。
- priority 等级。Java线程的等级是1~10,默认是5。需要注意的是:有些系统会忽略优先级,程序的正确性不能依赖线程的优先级。
- threadLocals 这些留到ThreadLocal模块再讲。
线程的终止(这个方法很重要)
线程的安全终止推荐使用如下方式:Thread.interrupt()
不要使用过期的supend,resume,stop。因为这些方法挂起了也是会占有CPU资源的,使用下面的方法替换掉这些过期的方法:
通信使用notify,wait等
停止用打断 Thread.interrupt()
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
// thread may be blocked in an I/O operation, 判断如果线程是否是IO类型的operation引起的blocked
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this); //这里算是一个回调
return;
}
}
}
interrupted = true;
// inform VM of interrupt
interrupt0();
}
private native void interrupt0();
最重要的逻辑是:interrupt0()
从下面的 registerNatives 函数的讲解可以看出:
它对应的是:
// jdk/src/share/javavm/export/jvm.h:254
JNIEXPORT void JNICALL
JVM_Interrupt(JNIEnv *env, jobject thread);
调用栈如下:
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_Interrupt");
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
oop java_thread = JNIHandles::resolve_non_null(jthread);
MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
// We need to re-resolve the java_thread, since a GC might have happened during the
// acquire of the lock
JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
if (thr != NULL) {
Thread::interrupt(thr);
}
JVM_END
然后Thread::interrupt(thr); 每个线程的调用都不一样。
void Thread::interrupt(Thread* thread) {
trace("interrupt", thread);
debug_only(check_for_dangling_thread_pointer(thread);)
os::interrupt(thread);
}
//os_linux.cpp
void os::interrupt(Thread* thread) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(),
"possibility of dangling Thread pointer");
//获取系统native线程对象
OSThread* osthread = thread->osthread();
if (!osthread->interrupted()) {
osthread->set_interrupted(true); //设置java线程的interrupted属性
// 内存屏障,使 osthread 的 interrupted 状态对其它线程立即可见
OrderAccess::fence();
//前文说过,线程调用了sleep方法,则通过unpark唤醒
ParkEvent * const slp = thread->_SleepEvent ;
if (slp != NULL) slp->unpark() ;
}
//_parker用于concurrent相关的锁,此处同样通过unpark唤醒
if (thread->is_Java_thread())
((JavaThread*)thread)->parker()->unpark();
//synchronized同步块和Object.wait() 唤醒
ParkEvent * ev = thread->_ParkEvent ;
if (ev != NULL) ev->unpark() ;
}
最后看出interrupt还是调用unpark来进行实现的。
线程的初始化
1)线程默认的命名,修改线程名称使用setName2)父子线程。Thread.currentThread()可以获取当前线程。
整个Thread最终初始化的调用函数都是下面这个方法:
/**
* Initializes a Thread. 构造一个Thread,这个构造函数是private的。
*/
private 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();
if (g == null) { // 如果没有指定ThreadGroup那么它就会自己去找出一个ThreadGroup
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) { //从security manager找出一个group
g = security.getThreadGroup();
}
/* If the security manager doesn't have a strong opinion
on the matter, use the parent thread group. */
// 大多数会使用父线程的thread group
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(
SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon(); // 父线程是daemon线程就创建daemon线程
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();
this.target = target;
setPriority(priority);
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 */
this.tid = nextThreadID(); //这是生成线程id ,代码也很简单:不断++而已。
}
/*
* Thread ID
*/
private final long tid;
/* For generating thread ID */
private static long threadSeqNumber;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
ThreadGroup
ThreadGroup 是对Java对Thread的一种组织方式。每一个ThreadGroup都是Thread的集合。
ThreadGroup是一个以树状结构维护的一个数据结构。再我们虚拟机一开始启动的时候就初始化一个Root ThreadGroup给我们,所有的线程都在这Root ThreadGroup下面。
ThreadGroup重要的属性如下:
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
/**
* 线程组的parent
*/
private final ThreadGroup parent;
String name;
/**
* 最大优先级
*/
int maxPriority;
boolean destroyed;
boolean daemon;
int nUnstartedThreads = 0;
int nthreads;
Thread threads[]; // 保留的线程数组
int ngroups;
ThreadGroup groups[]; // 子线程组
其中最重要的部分是:
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
/**
* 线程组的parent
*/
private final ThreadGroup parent;
Thread threads[]; // 保留的线程数组
ThreadGroup groups[]; // 子线程组
上面的代码片段也就是我们ThreadGroup的组织方式,如下图:
线程组是一个树,每个线程组初始化的时候都有一个parent。
一个线程可以访问自己的线程组information,但是不能访问自己线程组的parent thread group或者其它的thread groups。
获取当前线程
Thread.currentThread() 是静态方法,也就是说在哪里都可以通过Thread.currentThread() 获取当前线程。
Thread其它api
sleep()
yield() : 提醒调度器我愿意放弃当前资源,如果CPU不紧张,则会忽略这种提醒
getId(): 获取线程ID
设置线程上下文类加载器:
join()
当我们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。
join的原理
我们看看join的源码:public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && millis < Long.MAX_VALUE) {
millis++;
}
join(millis); //将毫秒,纳秒都转换成毫秒
}
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(final long millis)
throws InterruptedException { //最终执行的逻辑在这里。
// 首先整个方法使用了synchronized来修饰
if (millis > 0) {
if (isAlive()) { //判断这个线程是否是活的,如果是死的直接跳过去,上面都不执行。
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay); //最重要的是这个函数,这个函数是Object.wait。会将线程挂起,然后等待一个notify或者notifyAll
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0); //阻塞到超时
}
} else if (millis == 0) {
while (isAlive()) { //判断这个线程是否是活的,如果是死的直接跳过去,上面都不执行。
wait(0); //最重要的是这个函数,这个函数是Object.wait。会将线程挂起,然后等待一个notify或者notifyAll
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
/**
* Tests if this thread is alive. A thread is alive if it has
* been started and has not yet died.
* 测试这个线程是否是活的,如果这个线程已经被调用了started方法,那么它就是活的;要不然就是死的。
* @return {@code true} if this thread is alive;
* {@code false} otherwise.
*/
public final native boolean isAlive();
从上面可以看出最终执行的方法都是public final synchronized void join(final long millis)。
首先整个join方法用来 synchronized 来修饰,然后使用了Object.wait将线程挂起,然后等待一个notify或者notifyAll。
然后想一下join的注解介绍:阻塞到这个线程退出。我猜测在线程退出的时候,底层会调用一个notifyAll来唤醒所有的join的线程。
然后进入源码之中一看:
//thread.cpp 文件。
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
... //省略了代码
// Notify waiters on thread object. This has to be done after exit() is called
// on the thread (if the thread is the last thread in a daemon ThreadGroup the
// group should have the destroyed bit set before waiters are notified).
// 通知这个线程上的waiters。这个是在thread::exit退出之前就要做好的。
ensure_join(this);
... //省略了代码
然后我们进入ensure_join之中看看。
/**
* 下面是处理Thread.join 的notifyAll的逻辑。
*/
static void ensure_join(JavaThread* thread) {
// We do not need to grab the Threads_lock, since we are operating on ourself.
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
// Clear the native thread instance - this makes isAlive return false and allows the join()
// to complete once we've done the notify_all below
// 这里是清除native线程,这个操作会导致isAlive()方法返回false
java_lang_Thread::set_thread(threadObj(), NULL);
// 然后通知所有的waiters。
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
果然和我猜测的一样。
结论是:线程退出的时候,清除状态,这个时候调用isAlive() 就会返回false。然后调用lock.notify_all(thread); 这个时候就会唤醒线程上所有的wait,然后Thread.join里面的while循环就会继续。但是isAlive返回false了,这样就会退出循环整个查询逻辑结束。
看下面的案例
public class ThreadJoinDemo extends Thread{
int i;
Thread previousThread; //上一个线程
public ThreadJoinDemo(Thread previousThread,int i){
this.previousThread=previousThread;
this.i=i;
}
@Override
public void run() {
try {
//调用上一个线程的join方法,大家可以自己演示的时候可以把这行代码注释掉
previousThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("num:"+i);
}
public static void main(String[] args) {
Thread previousThread=Thread.currentThread();
for(int i=0;i<10;i++){
ThreadJoinDemo joinDemo=new ThreadJoinDemo(previousThread, i);
joinDemo.start();
previousThread=joinDemo;
}
System.out.println("我已经退出了");
}
}
控制台输出
我已经退出了
num:0
num:1
num:2
num:3
num:4
num:5
num:6
num:7
num:8
num:9
关闭线程
复制线程
registerNatives
/* 确保 registerNatives() 会在 clinit 中调用 */
private static native void registerNatives();
static {
registerNatives(); // registerNatives 是这个类使用到了native 方法,这样注册这个类的native方法。
}
static函数是在clinit之中执行的。
这是一个jni调用方法,最后注册的方法如下:
// jdk/src/share/native/java/lang/Thread.c:43
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
从上面可以看出interrupt0 对应的函数是:JVM_Interrupt。
// jdk/src/share/javavm/export/jvm.h:254
JNIEXPORT void JNICALL
JVM_Interrupt(JNIEnv *env, jobject thread);
多线程编程的一些基本概念
上下文切换
什么是上下文切换呢?我们的CPU因为运行速度比内存等快了好几个数量级,应对这种情况为了充分使用CPU,我们将CPU的处理时间切片,哪个时间单位归哪个线程执行任务,都是靠排队。每个线程执行任务是需要执行任务的环境的,就像我们工作的时候的工作环境准备。那么准备这些环境也是需要时间的,这样就会浪费了很多CPU执行的时间。所以多线程虽然能提升我们执行任务的效率,但是如果设置的线程数不合理也是有很大的性能消耗。
怎么设置线程数比较合理呢?看下图:
死锁
死锁演示的代码
public class DeadLockDemo {
private static Object lockA = new Object();
private static Object lockB = new Object();
private static class LockAThenB implements Runnable {
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName()+ "获得 lockA 锁");
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获得 lockB 锁");
}
}
}
}
private static class LockBThenA implements Runnable {
@Override
public void run() {
synchronized (lockB) {
System.out.println(Thread.currentThread().getName()+ "获得 lockA 锁");
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "获得 lockB 锁");
}
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
int count = 10;
while (count-- > 0) {
executorService.execute(new LockAThenB());
executorService.execute(new LockBThenA());
}
}
}
我们使用jstack 工具 可以看到 死锁里面具体内容
2020-07-06 09:44:12
Full thread dump OpenJDK 64-Bit Server VM (14.0.1+7 mixed mode, sharing):
.... // 省略了一堆代码。
JNI global refs: 16, weak refs: 0
Found one Java-level deadlock:
=============================
"pool-1-thread-1":
waiting to lock monitor 0x00007fd5c12f8f00 (object 0x000000070f537a48, a java.lang.Object),
which is held by "pool-1-thread-2"
"pool-1-thread-2":
waiting to lock monitor 0x00007fd5c12f7000 (object 0x000000070f537a38, a java.lang.Object),
which is held by "pool-1-thread-1"
Java stack information for the threads listed above:
===================================================
"pool-1-thread-1":
at com.taldh.mutlithread.deadlock.DeadLockDemo$LockAThenB.run(DeadLockDemo.java:26)
- waiting to lock <0x000000070f537a48> (a java.lang.Object)
- locked <0x000000070f537a38> (a java.lang.Object)
at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@14.0.1/ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@14.0.1/ThreadPoolExecutor.java:630)
at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)
"pool-1-thread-2":
at com.taldh.mutlithread.deadlock.DeadLockDemo$LockBThenA.run(DeadLockDemo.java:38)
- waiting to lock <0x000000070f537a38> (a java.lang.Object)
- locked <0x000000070f537a48> (a java.lang.Object)
at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@14.0.1/ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@14.0.1/ThreadPoolExecutor.java:630)
at java.lang.Thread.run(java.base@14.0.1/Thread.java:832)
Found 1 deadlock.
0x000000070f537a48 这些表示的是锁头的地址。
我是废物,大佬的文章抄完了 下面我自己写一下我对Thread类源码的理解
内部类和内部枚举类解析
枚举类 State解析
public enum State {
/**
* 初始态
* 线程创建完毕还未启动,未调用start方法
*/
NEW,
/**
* 包含两种状态
* 1.就绪态
* 2.运行态
*/
RUNNABLE,
/**
* 阻塞态
* synchronized会导致线程进入blocked状态
*/
BLOCKED,
/**
*
* 等待态
* 3种情况调用后会导致线程处于这个状态:
* 1.Object.wait
* 2.Thread.join
* 3.LockSupport.park
*
* 等待态的线程会等待其他线程执行特定的操作
*
* 例如一个线程调用了Object.wait之后进入等待态,另一个线程调用Object.notify或Object.notifyAll可以将其唤醒,被唤醒的线程需要获取对象的锁才能恢复执行
* 调用Thread.join会等待指定的线程终止
*/
WAITING,
/**
*
* 超时等待态
* 线程等待指定的时间再执行
* 5种情况调用后会导致线程处于这个状态:
* 1.Thread.sleep
* 2.Object.wait 传入等待时长
* 3.Thread.join 传入等待时长
* 4.LockSupport.parkNanos
* 5.LockSupport.parkUntil
*/
TIMED_WAITING,
/**
* 终止态
* 线程执行完毕
*/
TERMINATED;
}
初始态(NEW):
创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态
运行态(RUNNABLE):
运行态在Java中包括就绪态和运行态
1.就绪态:
- 该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运行
- 所有就绪态的线程存放在就绪队列中
2.运行态:
-
获得CPU执行权,正在执行的线程
-
由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一个运行态的线程
-
阻塞态(BLOCKED)
-
当一条正在执行的线程请求某一资源失败时,就会进入阻塞态
-
而在Java中,阻塞态专指请求锁失败时进入的状态
-
由一个阻塞队列存放所有阻塞态的线程。处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执行
等待态(WAITING)
- 当前线程中调用wait、join、park函数时,当前线程就会进入等待态
- 也有一个等待队列存放所有等待态的线程
- 线程处于等待态表示它需要等待其他线程的指示才能继续运行
- 进入等待态的线程会释放CPU执行权,并释放资源(如:锁)
超时等待态(TIMED_WAITING)
- 当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就会进入该状态
- 它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后被其他线程唤醒或超时自动唤醒
- 进入该状态后释放CPU执行权和占有的资源,其中wait()方法会释放CPU执行权和占有的锁,sleep(long)方法仅释放CPU使用权,锁仍然占用
- 与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁
终止态(TERMINATED)
-
线程执行结束后的状态
-
其中有几点需要注意的:
-
yield方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行
-
wait和notify必须配套使用,即必须使用同一把锁调用
-
wait和notify必须放在一个同步块中调用,wait和notify的对象必须是他们所处同步块的锁对象
Caches/WeakClassKey
我们可以看到WeakClassKey这个内部类继承了WeakReference,而WeakClassKey被Caches所使用,从名字我们也能明白其部分含义,本地缓存,WeakClassKey是弱引用相关类,至于弱引用的使用大家可以自行Google,这里不多说,如果你看过《深入理解Java虚拟机》,应该多少有点了解
subclassAudits提供了一个哈希表缓存,该缓存的键类型为java.lang.Thread.WeakClassKey,注意看它的值类型是一个java.lang.Boolean类型的,从其代码注释可以知道这个哈希表缓存中保存的是所有子类的代码执行安全性检测结果
subclassAuditsQueue定义了一个Queue队列,保存已经审核过的子类弱引用
/** cache of subclass security audit results */
/* Replace with ConcurrentReferenceHashMap when/if it appears in a future
* release */
private static class Caches {
/** cache of subclass security audit results */
// 缓存安全检查结果
static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
new ConcurrentHashMap<>();
// 队列
/** queue for WeakReferences to audited subclasses */
static final ReferenceQueue<Class<?>> subclassAuditsQueue =
new ReferenceQueue<>();
}
/**
* Weak key for Class objects.
**/
static class WeakClassKey extends WeakReference<Class<?>> {
/**
* saved value of the referent's identity hash code, to maintain
* a consistent hash code after the referent has been cleared
*/
private final int hash;
/**
* Create a new WeakClassKey to the given object, registered
* with a queue.
*/
WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
super(cl, refQueue);
hash = System.identityHashCode(cl);
}
/**
* Returns the identity hash code of the original referent.
*/
@Override
public int hashCode() {
return hash;
}
/**
* Returns true if the given object is this identical
* WeakClassKey instance, or, if this object's referent has not
* been cleared, if the given object is another WeakClassKey
* instance with the identical non-null referent as this one.
*/
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof WeakClassKey) {
Object referent = get();
return (referent != null) &&
(referent == ((WeakClassKey) obj).get());
} else {
return false;
}
}
}
继承的类和实现的接口
Runnable (你需要重写run方法)
出现最频繁的ThreadGroup类分析
线程组是一个父子结构,一个线程组可以属于其他线程组,也可以拥有自己的子线程组,如果你一直向上追溯的话,会发现所有的线程组都在一个根线程组里面— System 线程组
线程组的出现可不是为耍酷用的,它是为了更方便的管理线程而存在的.比如设置线程最大优先级,销毁线程等等,添加线程,移除线程组等
还继承了Thread.UncaughtExceptionHandler,用来处理默认的线程异常捕获处理,如果线程没有设置处理器,默认走的是group的处理方法
其中大部分方法都是递归操作方法
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
// 所属的父ThreadGroup
private final ThreadGroup parent;
// 线程组名称
String name;
// 最大优先级
int maxPriority;
/*是否被销毁*/
boolean destroyed;
// 是否是守护线程
boolean daemon;
//未启动线程数
int nUnstartedThreads = 0;
// 线程总数
int nthreads;
// 线程数组
Thread threads[];
// 线程组数量
int ngroups;
// 所拥有线程组数组
ThreadGroup groups[];
private ThreadGroup() { // called from C code
this.name = "system";
this.maxPriority = Thread.MAX_PRIORITY;
this.parent = null;
}
// 指定名字,父group为当前线程的线程组
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
// 指定父group和name的线程组
public ThreadGroup(ThreadGroup parent, String name) {
this(checkParentAccess(parent), parent, name);
}
// 处理线程未检查异常异常,默认走这里,单独设置了走自己的
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
// 获取默认的异常处理
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
// 如果有设置,则进行处理,否则直接打印System.err
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
// 设置最大优先级,递归
public final void setMaxPriority(int pri) {
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
checkAccess();
if (pri < Thread.MIN_PRIORITY || pri > Thread.MAX_PRIORITY) {
return;
}
// 设置最大优先级不能超过 父group的最大优先级
maxPriority = (parent != null) ? Math.min(pri, parent.maxPriority) : pri;
ngroupsSnapshot = ngroups;
if (groups != null) {
groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
} else {
groupsSnapshot = null;
}
}
// 递归调用该线程组的每个线程组
for (int i = 0 ; i < ngroupsSnapshot ; i++) {
groupsSnapshot[i].setMaxPriority(pri);
}
}
// 递归获取线程总数
public int activeCount() {
int result;
// Snapshot sub-group data so we don't hold this lock
// while our children are computing.
int ngroupsSnapshot;
ThreadGroup[] groupsSnapshot;
synchronized (this) {
if (destroyed) {
return 0;
}
// 线程总数
result = nthreads;
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;
}
.......add,remoev等其他方法
出现的属性和静态代码块解析
//装载本地库
private static native void registerNatives();
//每次调用的时候装载到本地库
static {
registerNatives();
}
//线程名称
// 线程名称
private volatile String name;
// 线程优先级
private int priority;
// 是否是守护线程,默认false
private boolean daemon = false;
/* JVM state */
private boolean stillborn = false;
// 要执行的任务
private Runnable target;
// 所属的线程群组
private ThreadGroup group;
// 每个线程实例都引用一个ThreadLocal的map,每个线程在向ThreadLocal里塞值的时候,其实都是向自己所持有的ThreadLocalMap里塞入数据
// 读数据同理,根据ThreadLocal引用作为key取出value,实现线程隔离
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal,自父线程集成而来的ThreadLocalMap,
* 主要用于父子线程间ThreadLocal变量的传递,实现类是InheritableThreadLocal类
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/* 用于自动编号匿名线程 */
private static int threadInitNumber;
// 同步方法,编号唯一
private static synchronized int nextThreadNum() { return threadInitNumber++; }
// 线程栈大小
private long stackSize;
// 线程ID
private long tid;
// 用于生成线程ID的序列号
private static long threadSeqNumber;
private volatile int threadStatus = 0;
// 获取下一个线程ID,同步
private static synchronized long nextThreadID() { return ++threadSeqNumber; }
// 线程最低优先级
public final static int MIN_PRIORITY = 1;
// 线程默认分配的优先级
public final static int NORM_PRIORITY = 5;
// 线程最高优先级
public final static int MAX_PRIORITY = 10;
构造器
// 线程无法克隆
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
创建线程的线程名字为nextThreadNum是当前最后创建的线程数+1
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
// 线程初始化 g 线程组名称 target 传入的Runnable(多态形式) name 线程的名称(没有名称会报错)
//stackSize 线程开辟的栈的大小
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// 线程名字不能为null
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//当前线程是他的父亲 如果你是main调用 那么main函数就是他的父类
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
// 每个线程都必须在一个线程组中,如果g为null,则设置为当前线程或者SecurityManager的线程组
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
// 检查是否具有需要的权限
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
// 增加线程组中未启动线程的数量
g.addUnstarted();
this.group = g;
// 设置优先级与是否是守护线程,与ThreadGroup相同
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();
this.target = target;
setPriority(priority);
// 如果父线程的inheritableThreadLocals不为null,则将父线程inheritableThreadLocals传递至子线程
// 实现类是InheritableThreadLocal,
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
// 调用ThreadLocal的createInheritedMap方法,复制父线程table到当前线程的inheritableThreadLocals
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
// 设置指定的栈大小,如果未指定大小,将在jvm 初始化参数中声明:Xss参数进行指定
this.stackSize = stackSize;
// 设置线程ID
tid = nextThreadID();
}
// 设置优先级
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);
}
}
// 设置线程优先级
private native void setPriority0(int newPriority);
// 构造器,所有构造器都调用init方法,每个线程都有所属的线程组
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); }
public Thread(String name) { init(null, null, name, 0); }
public Thread(ThreadGroup group, String name) { init(group, null, name, 0); }
public Thread(Runnable target, String name) {init(null, target, name, 0); }
public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); }
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); }