线程创建方式
- 继承Thread类.
- 实现Runable接口.
- 实现Callable接口.
Runable/Callable接口的实现, 都是重写其中的run/call方法, 实现任务逻辑, 再由线程执行器(可以是Thread类,也可以是线程池)并发执行run/call的逻辑.
而Thread类中的包含start方法, 可以控制线程启动,执行任务.
线程上下文切换(Thread Context Switch)
当发生线程上下文切换时, 操作系统会保存当前线程状态, 并且恢复另一个线程的运行. java中程序计数器就会记录当前线程下一个执行指令的地址, 当线程切换时, 依据这个地址继续执行程序. 这个程序计数器是线程私有的.
线程状态
NEW: 线程创建, 还没与操作系统中的线程关联
注意操作系统的线程状态定义与java中有所不同. 操作系统中的block是指, 调用了阻塞API比如IO读取文件操作, 操作系统的线程状态就会进入阻塞状态.
操作系统的可运行状态是指线程还没有获得cpu时间片
现成状态转换图:
Thread类中常见API
方法名 | 说明 | 备注 |
---|---|---|
start() void | 启动一个线程,现成执行run方法中逻辑 | 方法执行后, 新创建线程为就绪态 |
join() void | 阻塞等待当前线程执行结束 | |
join(long n) void | 阻塞等待当前线程执行结束, 最多等待n毫秒 | |
getId() long | 获取long类型线程ID | |
getName() String | 获取线程名称 | |
getPriority() int | 获取线程优先级 | 1 - 10 , 10优先级最高 |
setPriority(int i) | 设置线程优先级 | 最大 10, 最小1, 超出抛异常 |
getState() State | 获取线程状态枚举值 | State:{NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED} |
isInterrupted() boolean | 判断现成是否被中断 | |
interrupt() void | 打断线程 | sleep, waite, join时的线程, 如果调用interrupt方法会抛异常:InterruptedException, 并且清除中断标记(isInterrupted方法返回false), 如果是运行中的线程, 被打断, 会设置打断标记(isInterrupted方法返回false) |
isAlive() boolean | 判断现成是否存活 | 即未运行完毕的线程 |
sleep(long n) | 线程休眠,进入time_waiting状态 | |
yield() | 线程让出时间片 |
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 (isAlive()) {
wait(0);
}
} else {
// 设置超时时间,加入等待
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join案例
public class Test7 {
private static final Object obj = new Object();
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("执行逻辑");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t.start();
t.join();
}
}
在上面的案例代码中, t.join()方法会让main方法进入wait等待. 阻塞的是main方法. 当t1线程执行完run方法逻辑后. JVM底层就会调用notify方法, 唤醒持有t1锁对象的线程.(join()方法是被synchonized修饰的, 锁对象就是线程对象)
JVM唤醒join源码
// 位于/hotspot/src/share/vm/runtime/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).
// 有一个贼不起眼的一行代码,就是这行
ensure_join(this);
// ...
}
static void ensure_join(JavaThread* thread) {
// We do not need to grap 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
java_lang_Thread::set_thread(threadObj(), NULL);
// 同志们看到了没,别的不用看,就看这一句
// thread就是当前线程,是啥?就是刚才例子中说的t线程啊。
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
守护线程
Java提供了两种线程:守护线程和用户线程
当所有非守护线程结束时,程序就会终止,而不管守护线程是否还在运行。守护线程通常用于执行一些后台任务或服务,例如垃圾回收器(Garbage Collector)线程就是一个守护线程。
最主要的区别就是, 系统中某一时刻, 存在守护线程正在运行, 而没有用户线程运行. 那么JVM就会结束运行.