剖析JDK源码-Thread-(8)
一、简述
- 一个thread是程序中执行的线程。Java虚拟机允许一个应用程序有多个线程并发执行。
- 每个线程都有一个优先级。具有较高优先级的线程是优先于优先级较低的线程执行。
- 每个线程都可以标记为守护进程。
- 当代码运行的线程创建一个新的 thread 对象(新线程),新线程的优先级初始设置为创建线程的优先级。
- 当且仅当创建线程是守护进程时才是守护线程。
- 当所有非守护线程结束或死亡后,程序将停止 。
二、源码
1、类的声明和属性
- 实现Runnable类。
public class Thread implements Runnable {
}
- 注册
private static native void registerNatives();
static {
registerNatives();
}
- 被volatile修饰的线程名name,(确保每次读取的是真实的值,而不是寄存器的值)
- 优先级priority
private volatile String name;
private int priority;
private Thread threadQ;
private long eetop;
- 是否单步执行此线程,
private boolean single_step;
- 该线程是否为守护线程。
private boolean daemon = false;
- 虚拟机JVM 状态。
- 该线程将要执行的任务。
private boolean stillborn = false;
private Runnable target;
- 线程所在的组。(线程集合)
- 该线程的上下文类加载器。
- 该线程继承的AccessControlContext。(用于根据它封装的上下文做出系统资源访问决策)
private ThreadGroup group;
private ClassLoader contextClassLoader;
private AccessControlContext inheritedAccessControlContext;
- 用于匿名线程的自动编号。(每个线程都有唯一的编号)
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
- 属于这个线程的ThreadLocal值。这个映射由ThreadLocal类维护。
- 属于这个线程可被继承的ThreadLocal值。这个映射由ThreadLocal类维护。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
- 线程请求的堆栈大小,如果创建时没有指定堆栈大小,则默认为0。但有些虚拟机会忽略它。
- 在本机线程终止后持续存在的jvm私有状态。
private long stackSize;
private long nativeParkEventPointer;
- 当前线程ID,每个线程存在唯一的ID。
private long tid;
private static long threadSeqNumber;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
- 线程的状态,初始值为0——尚未启动。
private volatile int threadStatus = 0;
- 可设置的最低优先级为1。
- 默认的优先级为5。
- 可设置的最高优先级为10。
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;
- 返回对当前执行线程对象的引用。
public static native Thread currentThread();
2、 构造方法
Thread()
分配一个新的 Thread对象。
Thread(Runnable target)
分配一个新的 Thread对象。
Thread(Runnable target, String name)
分配一个新的 Thread对象。
Thread(String name)
分配一个新的 Thread对象。
Thread(ThreadGroup group, Runnable target)
分配一个新的 Thread对象。
Thread(ThreadGroup group, Runnable target, String name)
分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
分配一个新的 Thread对象,以便它具有 target作为其运行对象,将指定的 name正如其名,以及属于该线程组由称作 group ,并具有指定的 堆栈大小 。
Thread(ThreadGroup group, String name)
分配一个新的 Thread对象。
3、内部方法
- 对调度程序的一个暗示,作用是放弃当前线程获取CPU的执行权,将让其它的线程去获取。
public static native void yield();
- 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
- 使正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。
- 两者的区别是第二个加入了个纳秒的参数,用于调整睡眠时间的精确度。
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(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 >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
- 将CloneNotSupportedException作为线程抛出,重写的是Object类中的方法,此并无意义。
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
- 执行线程,java虚拟机默认调用此线程的run方法。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
- 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
@Override
public void run() {
if (target != null) {
target.run();
}
}
- 关闭线程,并且安全管理器已经允许进行退出操作。
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
target = null;
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
- stop()本质上是不安全的。 使用Thread.stop停止线程可以解锁所有已锁定的监视器(由于未ThreadDeath ThreadDeath异常在堆栈中ThreadDeath的自然结果)。 如果先前受这些监视器保护的任何对象处于不一致的状态,则损坏的对象将变得对其他线程可见,可能导致任意行为。 stop许多用途应该被替换为只是修改一些变量以指示目标线程应该停止运行的代码。 目标线程应该定期检查此变量,如果变量表示要停止运行,则以有序方式从其运行方法返回。 如果目标线程长时间等待(例如,在interrupt变量上),则应该使用interrupt方法来中断等待。
- stop(Throwable obj)最初设计为强制线程停止并抛出一个给定的Throwable作为例外。 它本质上是不安全的(有关详细信息,请参阅stop() ),此外还可用于生成目标线程未准备处理的异常。
两者都已被弃用
@Deprecated
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW", it can't change to
// not-NEW because we hold the lock.
if (threadStatus != 0) {
resume(); // Wake up thread if it was suspended; no-op otherwise
}
// The VM can handle all thread states
stop0(new ThreadDeath());
}
public final synchronized void stop(Throwable obj) {
throw new UnsupportedOperationException();
}
- 中断此线程
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
- 返回此线程是否被中断,
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);
- destroy()最初是为了销毁这个线程而没有任何清理。 它所持有的任何监视器都将保持锁定。 但是,该方法从未实现。 如果要实施,那么它将是suspend()的方式是僵死的 。 如果目标线程在销毁时保护关键系统资源的锁,则无法再次访问该资源。 如果另一个线程曾尝试锁定此资源,将导致死锁。 这种僵局通常表现为“冻结”过程。“没实现的理想”
@Deprecated
public void destroy() {
throw new NoSuchMethodError();
}
- 返回此线程的状态,是否还活着。
public final native boolean isAlive();
- suspend()和resume()是需要结合使用的,但是suspend()本身就是死锁的。 如果目标线程在挂起时保护关键系统资源的监视器上的锁定,则在目标线程恢复之前,线程不能访问该资源。 如果要恢复目标线程的线程在调用resume之前尝试锁定此监视器, resume导致死锁。 这种僵局通常表现为“冻结”过程。因此也被弃用。
- checkAccess() 确定当前正在运行的线程是否有权限修改此线程。
@Deprecated
public final void suspend() {
checkAccess();
suspend0();
}
@Deprecated
public final void resume() {
checkAccess();
resume0();
}
public final void checkAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkAccess(this);
}
}
- 返回当前线程的thread group及其子组中统计的活动线程数。
public static int activeCount() {
return currentThread().getThreadGroup().activeCount();
}
- 将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。
public static int enumerate(Thread tarray[]) {
return currentThread().getThreadGroup().enumerate(tarray);
}
- 等待这个线程死亡,可以设置最长等待时间,精准到毫秒millis、纳秒nanos。(在预定时间后死亡)
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");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
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 >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
public final void join() throws InterruptedException {
join(0);
}
- 将当前线程的堆栈跟踪打印到标准错误流。
public static void dumpStack() {
new Exception("Stack trace").printStackTrace();
}
- 返回此线程的字符串表示,包括线程的名称,优先级和线程组。
public String toString() {
ThreadGroup group = getThreadGroup();
if (group != null) {
return "Thread[" + getName() + "," + getPriority() + "," +
group.getName() + "]";
} else {
return "Thread[" + getName() + "," + getPriority() + "," +
"" + "]";
}
}
- 当且仅当当前线程在指定的对象上持有监视器锁时返回 true。
public static native boolean holdsLock(Object obj);
4、枚举
- 线程的状态
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 - 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
- 终止(TERMINATED):表示该线程已经执行完毕。
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
- 线程运行流图
三、常见面试题
1、线程和进程的区别?
-
性质:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一个进程含有至少一个线程。 -
应用范围:
使用进程目的在于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序。线程为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。
2、并行(parallellism)和并发(concurrency)有什么区别?
并行 | 并发 |
---|---|
两个或者多个事件在同一时刻发生; | 指两个或多个事件在同一时间间隔发生。 |
在不同实体上的多个事件, | 在同一实体上的多个事件。 |
3、守护线程是什么?
守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。
4、创建线程有哪几种方式?
创建线程有三种方式:
继承 Thread 重写 run 方法;
实现 Runnable 接口;
实现 Callable 接口。
5、线程有哪些状态?
线程的状态:
NEW 尚未启动
RUNNABLE 正在执行中
BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
WAITING 永久等待状态
TIMED_WAITING 等待指定的时间重新被唤醒的状态
TERMINATED 执行完成
6、线程的 run() 和 start() 有什么区别?
start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。