Thread 类是 JVM 用来管理线程的一个类
换句话说,每个线程都有一个唯一的 Thread 对象与之关联
Thread 类的对象就是用来描述一个线程的执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度、线程管理
Thread 的常见构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target , String name) | 使用 Runnable 对象创建线程对象,并命名 |
Thread(ThreadGroup group , Runnable target) | 线程可以被用来分组管理,分好的组即线程组 |
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
Thread 的几个常见属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台进程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
- ID 是线程的唯一标识,不同线程不会重复
- 名称是各种调试工具用到
- 状态表示线程当前所处的一个情况
- 优先级高的线程理论上来说更容易被调度到,和线程调度相关
- 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- 是否存活,即简单的理解,为 run 方法是否运行结束了。但是注意,此时Thread对象还没有销毁
- 是否被中断,在下面有
public class test2 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run(){
for(int i = 0 ; i < 5 ;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
System.out.println(t.getId());
System.out.println(t.getName());
System.out.println(t.getState());
System.out.println(t.getPriority());
System.out.println(t.isDaemon());
System.out.println(t.isAlive());
System.out.println(t.isInterrupted());
t.start();
System.out.println("==================");
System.out.println(t.getId());
System.out.println(t.getName());
System.out.println(t.getState());
System.out.println(t.getPriority());
System.out.println(t.isDaemon());
System.out.println(t.isAlive());
System.out.println(t.isInterrupted());
}
}
//运行结果
12
Thread-0
NEW
5
false
false
false
==================
12
Thread-0
RUNNABLE
5
false
true
false
run() 和 start() 的区别
- 调用 start 方法才是创建了线程,有了新的执行流
- 调用 run 方法只是一个普通的方法调用,不涉及新线程的创建(仍然是在原来的线程中)
获取当前线程的引用
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
中断一个线程
目前常见的有以下两种方式:
- 通过共享的标记来进行沟通
- 调用 interrupt() 方法来通知
示例一:
public class test3{
private static class MyRunnable implements Runnable {
public volatile boolean isQuit = false;
@Override
public void run() {
while (!isQuit) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "大毛");
System.out.println(Thread.currentThread().getName());
thread.start();
Thread.sleep(1 * 1000);
System.out.println(Thread.currentThread().getName());
target.isQuit = true;
}
}
示例2:
public class test4{
private static class MyRunnable implements Runnable {
@Override
public void run() {
// 两种方法均可以
while (!Thread.interrupted()) {
//while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName());
break;
}
}
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "二毛");
System.out.println(Thread.currentThread().getName());
thread.start();
Thread.sleep(1 * 1000);
System.out.println(Thread.currentThread().getName());
thread.interrupt();
}
}
这里说一下第二种情况:
-
通过 thread 对象调用 interrupt() 方法通知该线程停止运行
-
thread 收到通知的方式有两种:
- 如果线程调用了 wait / join / sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
- 否则,只是内部的一个中断标志被设置,thread 可以通过
- Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
- Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
示例3:
public class test5{
private static class MyRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("通过异常收到了中断情况");
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "大明");
thread.start();
thread.interrupt();
}
}
//运行结果——通过异常收到中断通知,并且标志位被清
false
false
false
false
false
示例4:
public class test6{
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.interrupted());
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "二明");
thread.start();
thread.interrupt();
}
}
//运行结果 —— 只有一开始是 true,后边都是 false,因为标志位被清
true
false
false
false
false
示例5:
public class test7{
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "小明");
thread.start();
thread.interrupt();
}
}
//运行结果 —— 全部是 true,因为标志位没有被清
true
true
true
true
true
方法:
方法 | 说明 |
---|---|
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
等待一个线程 — join()
当多个线程被创建的时候,每个线程都是一个独立的执行流。这些线程中执行的先后顺序是不确定的,取决于系统的调度
等待线程机制就是无法保证哪个线程先执行,但是可以确定线程的“结束顺序”
public class test8 {
public static void main(String[] args) throws InterruptedException {
Runnable target = () -> {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
};
Thread thread1 = new Thread(target, "线程1");
Thread thread2 = new Thread(target, "线程2");
System.out.println("线程1开始工作");
thread1.start();
thread1.join();
System.out.println("线程1结束,线程2开始工作");
thread2.start();
thread2.join();
System.out.println("线程2结束");
}
}
//运行结果
线程1开始工作
线程1
线程1
线程1
线程1
线程1
线程1
线程1结束,线程2开始工作
线程2
线程2
线程2
线程2
线程2
线程2
线程2结束
因为, thread1先调用了 join(),所以要等 thread1 结束之后thread2才能开始工作
方法 | 说明 |
---|---|
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等 millis 毫秒 |
public void join(long millis, int nanos) | 同理 |
休眠当前线程 — sleep()
因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的
方法 | 说明 |
---|---|
public static void sleep(long millis) throws InterruptedException | 休眠当前线程millis毫秒 |
public static void sleep(long millis, int nanos) throws InterruptedException | 可以更高精度的休眠 |
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3*1000);
System.out.println(System.currentTimeMillis());
}
}
没有调用 sleep() 的线程随时可能会被调度到CPU上执行
当某线程调用sleep()后,就会进入等待状态。