目录
一、创建线程
1.继承 Thread 类
2. 实现 Runnable 接口
3.匿名内部类创建 Thread 子类对象
4. 匿名内部类创建 Runnable 子类对象
5. lambda 表达式创建 Runnable 子类对象
二、Thread 类及常见方法
2.1 Thread 的常见构造方法
2.2 Thread 的几个常见属性
2.3 启动一个线程-start()
2.4 中断一个线程
2.5 等待一个线程-join()
三. 线程的状态
若想了解线程的基本概念,请参照另一篇博文 :进程和线程
一、创建线程
1.继承 Thread 类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
2) 创建 MyThread 类的实例
Thread t = new MyThread();
3) 调用 start 方法启动线程
t.start(); // 线程开始运行
2. 实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
2) 创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.
Thread t = new Thread(new MyRunnable());
//或者
Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
3) 调用 start 方法
t.start(); // 线程开始运行
对比上面两种方法:
- 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
- 实现 Runnable 接口, this 表示的是 MyRunnable 的引用. 需要使用 Thread.currentThread(),当前线程实例对象。
3.匿名内部类创建 Thread 子类对象
在创建Thread类的对象时使用匿名内部类,并重写方法。
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
4. 匿名内部类创建 Runnable 子类对象
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
5. lambda 表达式创建 Runnable 子类对象
该方法是最简便创建线程的方法, lambda 表达式本质上是一个函数式接口(一个接口中只有一个抽象方法),自身就是run方法,表示逻辑。
lambda 表达式可以理解为:匿名内部类的简化,实际上为创建了一个类,实现接口,并重写方法。
//eg: (函数式接口参数)-> 表达式 / {代码块};
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
二、Thread 类及常见方法
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
2.1 Thread 的常见构造方法
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
2.2 Thread 的几个常见属性
- getId() ,ID 是线程的唯一标识,不同线程不会重复
- getName(),名称是各种调试工具用到
- getState(),状态表示线程当前所处的一个情况,下面我们会进一步说明
- getPriority(),优先级高的线程理论上来说更容易被调度到
- isDaemon() ,关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- isAlive() ,是否存活,即简单的理解,为 run 方法是否运行结束了
- isInterrupted() ,线程中断。
Thread.currentThread(),返回当前线程实例对象。
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": 我还
活着");
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我即将死去");
});
System.out.println(Thread.currentThread().getName()
+ ": ID: " + thread.getId());
System.out.println(Thread.currentThread().getName()
+ ": 名称: " + thread.getName());
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
System.out.println(Thread.currentThread().getName()
+ ": 优先级: " + thread.getPriority());
System.out.println(Thread.currentThread().getName()
+ ": 后台线程: " + thread.isDaemon());
System.out.println(Thread.currentThread().getName()
+ ": 活着: " + thread.isAlive());
System.out.println(Thread.currentThread().getName()
+ ": 被中断: " + thread.isInterrupted());
thread.start();
while (thread.isAlive()) {}
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
}
}
2.3 启动一个线程-start()
之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
start()内部是会调用到系统api,在系统内核中创建线程
run(),单纯描述了该线程要执行什么工作。
本质区别 在于是否在系统内核中创建了一个线程。
2.4 中断一个线程
interrupt中断,让一个线程停止运行run方法,
目前常见的有以下两种方式:
- 通过共享的标记来进行沟通,手动设置标志位。
- 调用 interrupt() 方法来通知,Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位。
thread 收到通知的方式有两种:
1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
- 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程.
2. 否则,只是内部的一个中断标志被设置, thread 可以通过
- Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
- Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。
如下代码就是第一种 因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志,所以线程run并没有结束,可以抛出异常break解决。
package thread;
// 线程终止
public class Demo9 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
// Thread 类内部, 有一个现成的标志位, 可以用来判定当前的循环是否要结束.
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程工作中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 1. 假装没听见, 循环继续正常执行.
e.printStackTrace();
// 2. 加上一个 break, 表示让线程立即结束.
// break;
// 3. 做一些其他工作, 完成之后再结束.
// 其他工作的代码放到这里.
break;
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("让 t 线程终止. ");
t.interrupt();
}
}
2.5 等待一个线程-join()
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。这时我们需要一个方法明确等待线程的结束。
主线程中,调用t.join() => 主线程等待 t 线程先结束。
三. 线程的状态
线程的状态是一个枚举类型 Thread.State
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
- NEW: 安排了工作, 还未开始行动
- RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
- BLOCKED: 这几个都表示排队等着其他事情
- WAITING: 这几个都表示排队等着其他事情
- TIMED_WAITING: 这几个都表示排队等着其他事情
- TERMINATED: 工作完成了.
//示例
package thread;
public class Demo12 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 在调用 start 之前获取状态, 此时就是 NEW 状态
System.out.println(t.getState());
t.start();
for (int i = 0; i < 5; i++) {
System.out.println(t.getState());
Thread.sleep(1000);
}
t.join();
// 在线程执行结束之后, 获取线程的状态, 此时是 TERMINATED 状态
System.out.println(t.getState());
}
}
结语:Thread类及常见方法的相关分享到这里就结束了,希望对大家的学习会有帮助,如果大家有什么问题或者不同的见解,欢迎大家评论区的留言, 感谢支持 !!!