Thread 类
Thread 类是系统自带的线程类,实现了 Runnable 接口。
线程定义
Runnable 接口内唯一声明了 run 方法,由 Thread 类实现。开发者在 run 方法中定义运行时线程将要执行的功能,线程开启后由 JVM 自动调用并执行。如果开发者主动调用 run 方法,只会当作普通方法执行。
开发者可以通过以下两种方式自定义线程类:
- 继承 Thread 类,重写 run 方法。
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}Copy to clipboardErrorCopied
- 实现 Runnable 接口,实现 run 方法。推荐使用,避免了单继承的局限性。
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}Copy to clipboardErrorCopied
线程启动
Thread 类定义了 start 方法。调用 start 方法后,系统会开启一个新线程进入就绪状态:由 JVM 会自动对线程进行调度,在运行时调用并执行线程的 run 方法。一个线程只能启动一次。
- 如果自定义线程类继承 Thread 类,直接启动。
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread("ThreadName");
t1.start();
t2.start();
}
}Copy to clipboardErrorCopied
- 如果自定义线程类实现 Runnable 接口,则需要借助 Thread 类启动线程。
public class Main {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t1 = new Thread(mythread); // 由系统指定默认线程名 Thread-X
Thread t2 = new Thread(mythread, "ThreadName"); // 开发者自定义线程名
t1.start();
t2.start();
}
}Copy to clipboardErrorCopied
同时定义和启动线程
通过匿名内部类方式,我们可以实现同时定义和启动线程的简洁写法。
代码语言:javascript复制public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable(){
public void run(){
System.out.println(Thread.currentThread().getName());
}
}).start();
}
}
// Lambda 表达式简写一
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
}).start();
}
}
// Lambda 表达式简写二
public class Main {
public static void main(String[] args) {
Test test = new test();
Thread t = new Thread(test::method);
t.start();
}
}
class Test {
public void method() {
System.out.println(Thread.currentThread().getName());
}
}Copy to clipboardErrorCopied
线程弹出执行
Thread 类定义了 yield 方法。当前线程执行到 Thread.yield()
方法,会停止运行进入就绪状态。但线程切换到就绪状态后,什么时候被 JVM 调度回运行状态开发者无法控制。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
t1.start();
t2.start();
}
static class MyThread implements Runnable {
@Override
public void run() {
int count = 0;
for (int i = 0; i < 10000; i ) {
Thread.yield(); // 切换到就绪状态
count ;
System.out.println(count);
}
}
}
}Copy to clipboardErrorCopied
线程暂停执行
Thread 类定义了 sleep 方法。当前线程执行到 Thread.sleep(1000)
方法,会停止运行进入阻塞状态,但仍会保持对象锁,其他线程不可访问其资源。直到超时后进入就绪状态。调用 sleep 方法需要捕获或抛出 InterruptException 异常。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
t1.start();
t2.start();
}
static class MyThread implements Runnable {
@Override
public void run() {
int count = 0;
for (int i = 0; i < 10000; i ) {
try{
Thread.sleep(1000); // 当前线程暂停 1s
} catch(InterruptException e){
e.printStackTrace();
}
count ;
System.out.println(count);
}
}
}
}Copy to clipboardErrorCopied
线程交互
Object 类定义了 wait 和 notify 方法,通常被用于线程间交互/通信。必须在同步环境下(synchronized)使用,否则会抛出 IllegalMonitorStateException 异常。假定 obj 为同步环境上锁的对象:
线程等待
当前线程执行 obj.wait()
方法,线程会停止运行并释放对象锁 obj,其他线程可以访问其资源。同时线程进入 obj 对象的等待池,直到被 notify 方法唤醒进入就绪状态。调用 wait 方法需要捕获或抛出 InterruptException 异常。
wait 方法允许计时等待。当前线程执行 obj.wait(1000)
方法,计时结束后线程会被自动唤醒进入就绪状态。
线程唤醒
当前线程执行 obj.notify()
方法,会随机从 obj 对象等待池中选择一个线程唤醒,使其进入就绪状态。但是 notify 方法不会释放当前进程的对象锁,如果该线程持有 obj 对象的锁,当前线程释放锁后被唤醒的其他线程才能被执行。如果想被唤醒线程先执行,notify 方法后添加 wait 方法释放锁。
当前线程执行 obj.notifyall()
方法,会将所有 obj 对象等待池中所有线程唤醒。
public class ThreadDemo {
public static void main(String[] args) {
MyThread t = new MyThread("t");
synchronized(t) { // 对 t 设置对象锁
try {
t.start();
System.out.println("1");
t.wait(); // 当前线程释放 t 锁,进入 t 对象等待池
System.out.println("4");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyThread extends Thread {
@Override
public void run() {
synchronized (this) { // 对 t 设置对象锁
Thread.sleep(1000);
System.out.println("2");
this.notify(); // 随机唤醒一个 t 对象等待池中的线程
System.out.println("3");
}
}
}
}Copy to clipboardErrorCopied
其他线程优先
Thread 类定义了 join 方法,其底层通过调用 wait 方法实现。当前线程执行 t.join()
方法,线程会停止运行并释放对象锁,同时线程进入线程 t 对象的等待池,直到被唤醒进入就绪状态。通常用于主线程 main,等到线程 t 终止时自动被唤醒。调用 join 方法需要捕获或抛出 InterruptException 异常。
同样允许计时等待。当前线程执行 t.join(1000)
方法,计时结束后线程会被自动唤醒进入就绪状态,无须等待子线程结束时唤醒。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t = new Thread(mythread);
t.start();
System.out.println("1");
t.join(); // 子线程结束后才被唤醒
System.out.println("3");
}
static class MyThread implements Runnable {
@Override
public void run() {
Thread.sleep(1000);
System.out.println("2");
}
}
}Copy to clipboardErrorCopied
代码语言:javascript复制public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t = new Thread(mythread);
t.start();
System.out.println("1");
t.join(500); // 0.5s 后自动被唤醒
System.out.println("2");
}
static class MyThread implements Runnable {
@Override
public void run() {
Thread.sleep(1000);
System.out.println("3");
}
}
}Copy to clipboardErrorCopied
如果我们手动给线程 t 加锁,即使计时结束后线程被唤醒进入就绪状态,但仍无法立刻拿到锁进入运行状态。
代码语言:javascript复制public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t = new Thread(mythread);
t.start();
System.out.println("1");
t.join(500); // 自动被唤醒后仍要等待线程 t 释放锁
System.out.println("3");
}
static class MyThread implements Runnable {
@Override
public void run() {
synchronized (currentThread()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("2");
}
}
}
}Copy to clipboardErrorCopied
线程中断
调用 t.stop()
方法可以强制终止线程 t 运行,但强制中断线程可能会造成意想不到的问题,已不推荐使用。
目前主要采用设置线程中断标志的方式,向线程发送中止信号。由线程自行终止运行:
- 执行
t.interrupt()
方法,将线程 t 中断标志设为 true 。 - 执行
t.isInterrupted()
方法,查看线程 t 中断标志。 - 执行
t.interrupted()
方法,查看线程 t 中断标志然后将其设为 false 。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t = new Thread(mythread);
t.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
t.interrupt(); // 设置中断标志为 true
}
static class MyThread implements Runnable {
@Override
public void run() {
while (true) {
System.out.print("hello");
if(this.isInterrupted()){ // 查看中断标志,若为 true 结束循环
break;
}
}
}
}
}Copy to clipboardErrorCopied
捕获异常
调用 t.interrupt()
方法时如果线程 t 处在阻塞/等待状态,会立即退出阻塞/等待状态,并抛出 InterruptedException 异常。因此在调用 sleep/wait/join 方法时需要捕获 InterruptedException 异常。
开发者经常用 t.interrupt()
方法使线程退出阻塞/等待状态,同时也可以在捕获异常时使线程立即终止。
public class ThreadDemo {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread t = new Thread(mythread);
t.start();
System.out.println("1");
t.interrupt(); // 设置中断标志,使线程 t 退出等待状态
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
System.out.println("3");
}
static class MyThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) { // 被设置中断标志后会自动抛出异常
System.out.println("2");
return; // 捕获异常后终止线程
}
}
}
}