1、Thread类是什么
Java中Thread类用来实现线程编程
什么是线程、什么是进程、为什么用到并发可以查看我的并发系列第一篇文章《并发编程(一)为什么用并发编程、JMM内存模型、volatile、内存屏障》
2、线程的状态
new java.lang.Thread().start()只有调用start()方法的时候,才会真正的在 JVM中去创建线程。
线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:
- 创建:new
- 运行:runnable(运行中:running、就绪:ready)
- 等待:waiting(等待超时:timed waiting)
- 阻塞:blocked
- 终止:terminated
内部类:线程的状态
public enum State {
//初始状态,刚被new出来,还没调用start时的状态
NEW,
//运行时状态,或者就绪时状态,调用start后的状态
RUNNABLE,
//等待获取对象锁时的状态
BLOCKED,
//等待被人唤醒状态时状态
WAITING,
//等待别人唤醒,但设置了最长等待时间的状态
TIMED_WAITING,
//run方法运行结束的状态
TERMINATED;
}

并发指的是多个任务交替进行。对于线程的上下文切换实际上就是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。线程切换时同样会带来一定的开销代价
3、Thread类的方法

用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成
3.1、静态registerNatives方法:注册的方法
参考博文:java registernatives_Java 之 native 关键字与 registerNatives 方法
/**
* 先定义了registerNatives()方法,然后当该类被加载的时候,调用该方法完成对该类中本地方法的注册。
* 当包含registerNatives()方法的类被加载的时候,注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法。
*/
private static native void registerNatives();
static {
registerNatives();
}
3.2、init方法:初始化方法
/**
* Initializes a Thread.
*
* @param g 线程组:表示一组线程
* @param target 调用其run()方法的对象
* @param name 新线程的名字
* @param stackSize 新线程所需的堆栈大小,零表示忽略此参数。
* @param acc 要继承的AccessControlContext,如果为null,则为AccessController.getContext()
* @param inheritThreadLocals 如果{@code true},则从构造线程 继承 可继承线程局部变量的初始值
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// 线程名字(指定或默认线程名:"Thread-" + nextThreadNum())
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
// 获取当前调用的线程,把当前调用的线程作为父线程
java.lang.Thread parent = currentThread();
// 获取系统安全接口。
// 如果构造方法传入的线程组为null,则通过这个流程来决定其线程组,通常,新建线程的线程组为其创建线程的线程组
SecurityManager security = System.getSecurityManager();
// 新建线程的线程组
if (g == null) {
if (security != null) {
// 如果安全控制器不为null,线程组设置为当前线程的线程组
g = security.getThreadGroup();
}
if (g == null) {
//如果安全控制器的线程组为null 使用父线程的线程组
g = parent.getThreadGroup();
}
}
// 检查线程组是否需要访问权限
g.checkAccess();
// 检查是否有请求权限
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
// 把线程组的unstarted的线程数量加1
g.addUnstarted();
// 设置线程组为上面获取的线程组
this.group = g;
/* 设置当前线程是否为守护线程,默认是和当前类的ThreadGroup设置相
* 同。如果是守护线程的话,当前线程结束会随着主线程的退出而退出。
* jvm退出的标识是,当前系统没有活跃的非守护线程。
*
* 把线程的daemon设置为父线程的damon
*/
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();
// 设置Runnable对象,为传入的Runnable对象
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 */
/*设置指定的栈大小,如果未指定大小,将在jvm 初始化参数中声明:Xss参数进行指定*/
this.stackSize = stackSize;
/* Set thread ID */
// 产生新的tid并设置
tid = nextThreadID();
}
3.3、setPriority 方法:设置线程的优先级
//设置线程的优先级
public final void setPriority(int newPriority) {
//当前线程的线程组
ThreadGroup g;
//检查访问权限
checkAccess();
//参数校验
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
//如果当前线程组不为null
if (newPriority > g.getMaxPriority()) {
//传入的新优先级大于线程组最大的优先级
//把新的优先级设置为线程组最大的优先级
newPriority = g.getMaxPriority();
}
//设置线程的优先级
setPriority0(priority = newPriority);
}
}
//真正的设置线程优先级方法,这个是native方法
private native void setPriority0(int newPriority);
3.4、nextThreadNum 方法:获取下一个线程号,这个是静态同步方法
//获取下一个线程号,这个是静态同步方法
private static synchronized int nextThreadNum() {
//直接返回threadInitNumber,然后再把threadInitNumber加1
return threadInitNumber++;
}
3.5、nextThreadID 方法:获取下一个线程id,这是个静态同步方法
//获取下一个线程id,这是个静态同步方法
private static synchronized long nextThreadID() {
//先把threadSeqNumber加1,再返回
return ++threadSeqNumber;
}
3.6、yield方法
使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
public static native void yield();
3.7、sleep 方法:让当前线程休眠多少毫秒
//让当前线程休眠多少毫秒
//当前线程不会让出对象的锁让其他线程执行
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);
}
3.8、clone 方法
//不支持克隆,直接抛出异常
//这种方式要学习,JDK中有很多这种操作。子类不支持父类方法,直接抛出异常
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
3.9、start 方法:开始一个线程,只是把线程的状态从NEW变成了Runnable状态
//开始一个线程
//只是把线程的状态从NEW变成了Runnable状态
//并不一定立即执行,要看能不能抢到cpu的资源
public synchronized void start() {
if (threadStatus != 0)
//NEW状态为0
//如果当前线程不是NEW状态,抛出异常
throw new IllegalThreadStateException();
//唤醒线程组,把当前线程加入到线程组的线程数组中去
//把线程组的线程数量加1,并把线程组的unstarted的线程数量减1
group.add(this);
//设置启动状态为false
boolean started = false;
try {
//真正的启动线程方法,这个是一个native方法
start0();
//启动线程成功,把启动状态置为true
started = true;
} finally {
try {
if (!started) {
//如果启动失败,通知线程组做出相应的操作
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//真正启动线程的方法,是一个native方法
//可以看出启动线程操作是JVM去处理的
private native void start0();
3.10、run 方法:执行具体业务的方法
//执行具体业务的方法,这个应该是JVM底层回调
@Override
public void run() {
//直接交给Runnable对象去执行
if (target != null) {
target.run();
}
}
3.11、exit 方法:在Thread退出之前,系统调用exit来回收资源
//在Thread退出之前,系统调用exit来回收资源
private void exit() {
if (group != null) {
//如果线程组不为null
//唤醒线程组的其他线程
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
3.12、stop 方法:强制停止当前线程
//强制停止当前线程
public final void stop() {
//获取安全管理器
SecurityManager security = System.getSecurityManager();
if (security != null) {
//如果安全管理器不为null
//检查权限
checkAccess();
if (this != Thread.currentThread()) {
//如果不是当前线程自己调用
//检查权限
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
if (threadStatus != 0) {
//如果当前线程的状态不是NEW,可能处于暂停状态
//调用resume方法恢复一个暂停的线程
resume(); // Wake up thread if it was suspended; no-op otherwise
}
//VM可以操作所有的状态
//通过stop0来执行
stop0(new ThreadDeath());
}
//强制停止当前线程,这个是一个native方法
private native void stop0(Object o);
3.13、resume 方法:恢复一个暂停的线程,如果线程没有暂停,不做其他操作
//恢复一个暂停的线程,如果线程没有暂停,不做其他操作
@Deprecated
public final void resume() {
//检查权限
checkAccess();
//调用reume0来恢复暂停的线程
resume0();
}
//恢复暂停的线程,这个是一个nateive方法
private native void resume0();
3.14、interrupt 方法:设置线程中断信号
//中断线程
public void interrupt() {
if (this != Thread.currentThread())
//如果不是当前线程自己调用
//检查权限
checkAccess();
//同步blockerLock
//同步代码块里包含blocker,因为blockerLock就是为了同步blocker
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
//如果blocker不为null,即现在处于I/O阻塞状态
//仅仅设置interrupt状态
interrupt0();
//设置interrupt status后调用blocker的interrup方法
b.interrupt(this);
return;
}
}
//设置interrupt status
interrupt0();
}
//设置interrupt status 这是一个native方法
private native void interrupt0();
3.15、interrupted 方法:测试调用线程是否处于中断状态
- 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
- 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。被设置中断标志的线程将继续正常运行,不受影响。
- interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。
- 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
- 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。
// 测试当前线程是否处于中断状态
public boolean isInterrupted() {
//直接调用isInterrupted方法
return isInterrupted(false);
}
//测试线程是否中断,这是一个native方法
//ClearInterrupted true 会重置interrupted state, false 不会重置interrupted state
private native boolean isInterrupted(boolean ClearInterrupted);
3.16、suspend 方法:暂停线程
//暂停线程
@Deprecated
public final void suspend() {
//检查权限
checkAccess();
//调用susupend0来暂停线程
suspend0();
}
//暂停线程,这是一个native方法
private native void suspend0();
3.17、setName 方法:设置线程名称,这是一个同步方法
//设置线程名称,这是一个同步方法
public final synchronized void setName(String name) {
//检查权限
checkAccess();
//参数检查
if (name == null) {
throw new NullPointerException("name cannot be null");
}
//为name属性赋值
this.name = name;
if (threadStatus != 0) {
//如果当前线程不是NEW状态,调用setNativeName方法
setNativeName(name);
}
}
//设置线程名称
private native void setNativeName(String name);
3.18、join 方法:等待这个线程结束后再执行
//等待这个线程结束后再执行
public final void join() throws InterruptedException {
//调用下面的方法
join(0);
}
//等待指定最大时间或者这个线程执行结束再执行
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);
}
//这个才是真正的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");
}
if (millis == 0) {
//不指定等待时间的情况
//一直while循环,直到当前线程结束再执行调用线程
while (isAlive()) {
wait(0);
}
} else {
//指定等待时间的情况
//while循环判断当前线程是否结束
while (isAlive()) {
long delay = millis - now;
//如果等待时间超过指定时间退出,继续执行调用线程
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
3.19、setDaemon 方法:设置线程是守护线程还是用户线程
//设置线程是守护线程还是用户线程
//必须在start之前调用
public final void setDaemon(boolean on) {
//检查权限
checkAccess();
//如果线程在运行抛出异常
if (isAlive()) {
throw new IllegalThreadStateException();
}
//设置线程标记
daemon = on;
}
4、示例
4.0、三种实现线程的方式:继承Thear、实现Runnable、实现Callable(有返回值)
/**
* 三种实现线程的方式
*/
public class DemoExecutor extends Thread {
public DemoExecutor(String name) {
super(name);
}
@Override
public void run() {
System.out.println("Thread继承方式运行");
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
DemoExecutor executor1 = new DemoExecutor("张三");
executor1.start();
Thread thread = new Thread(new RunnableDemo(),"李四");
thread.start();
// 有返回方式运行
CallableDemo callableDemo = new CallableDemo();
FutureTask<Integer> callableTask = new FutureTask<>(callableDemo);
Thread thread1 = new Thread(callableTask);
thread1.start();
System.out.println("callableTask="+callableTask.get());
}
/**
* Runnable方式实现线程,不可抛出异常
*/
static class RunnableDemo implements Runnable{
@Override
public void run() {
System.out.println("Runnable实现方式运行");
}
}
/**
* 多线程返回值,可抛异常
*/
static class CallableDemo implements Callable<Integer>{
@Override
public Integer call() {
System.out.println("Callable实现方式运行");
return 1;
}
}
}
4.1、run、yield、start
public class DemoExecutor extends Thread {
public DemoExecutor(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i == 30) {
// 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)
this.yield();
}
}
}
public static void main(String[] args) {
DemoExecutor executor1 = new DemoExecutor("张三");
DemoExecutor executor2 = new DemoExecutor("李四");
executor1.start();
executor2.start();
}
}

4.2、isAlive、join、sleep
public class DemoExecutor extends Thread {
public DemoExecutor(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 150; i++) {
if(i == 150){
System.out.println("" + this.getName() + "-----" + i);
}
}
}
public static void main(String[] args) throws InterruptedException {
DemoExecutor executor1 = new DemoExecutor("张三");
DemoExecutor executor2 = new DemoExecutor("李四");
executor1.start();
executor2.start();
// 判断线程是否活动
boolean alive = executor2.isAlive();
// 等待线程2执行完成后在执行
executor2.join();
// 判断线程是否活动
boolean alive1 = executor2.isAlive();
System.out.println("alive="+alive+",alive1="+alive1+",currentTime="+ System.currentTimeMillis());
// 睡眠1秒
DemoExecutor.sleep(1000);
System.out.println("currentTime="+ System.currentTimeMillis());
}
}
4.3、interrupt、isInterrupted
- 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
- 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。被设置中断标志的线程将继续正常运行,不受影响。
- interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。
- 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
- 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。
public class DemoExecutor extends Thread {
public DemoExecutor(String name) {
super(name);
}
@Override
public void run() {
int i = 1;
while (true){
System.out.println("" + this.getName() + "-----" + i);
i++;
// 判断线程中断标识
if(isInterrupted()){
System.out.println("interrupted:"+isInterrupted());
return;
}
}
}
public static void main(String[] args) throws InterruptedException {
DemoExecutor executor2 = new DemoExecutor("李四");
executor2.start();
// 判断线程是否活动
boolean alive = executor2.isAlive();
// 设置线程中断标识
executor2.interrupt();
Thread.sleep(1000);
boolean alive1 = executor2.isAlive();
System.out.println("alive="+alive+",alive1="+alive1+",currentTime="+ System.currentTimeMillis());
}
}

本文深入解析Java中的Thread类,涵盖线程状态、线程生命周期、常用方法如start、run、sleep、interrupt等,并通过实例演示线程的创建与控制,同时探讨并发编程中的线程管理与上下文切换概念。
1438

被折叠的 条评论
为什么被折叠?



