创建多线程
创建多线程有多种形式, 此处介绍五种
继承 Thread 类
class MyThread extends Thread { @Override public void run() { System.out.println("hello world!1"); } } public class Main{ public static void main(String[] args) { // 继承 Thread 方法 MyThread t = new MyThread(); t.start(); } }
实现 Runnable 接口
class MyRunnable implements Runnable { @Override public void run() { System.out.println("hello runnable!"); } } public class Main{ public static void main(String[] args) { // 实现 Runnable 接口 Thread t = new Thread(new MyRunnable()); t.start(); } }
匿名内部类, 继承 Thread 类
public class Main{ public static void main(String[] args) { // 匿名内部类, 继承 Thread 类 // 1. 创建了一个 Thread 的子类 (子类没有名字, 因此叫 "匿名") // 2. 创建了子类的实例, 并且让 t 指向该实例 Thread t = new Thread() { @Override public void run() { System.out.println("hello "); } }; t.start(); } }
匿名内部类实现 Runnable 接口
public class Main{ public static void main(String[] args) { // 匿名内部类, 实现 Runnable 接口 // 1. 创建了一个类实现 Runnable // 2. 创建了类的实例, 并且传给 Thread 的构造方法 Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("hello 匿名内部类!"); } }); t.start(); } }
lambda 表达式
public class Main{ public static void main(String[] args) { // lambda 表达式 Thread t = new Thread(() -> { System.out.println("hello world!"); }); t.start(); } }
线程的状态
线程是CPU调度的基本单位, 所以这里的状态, 也是指 Java 中线程的状态, 可以使用 Thread.getState() 方法可以获取线程的状态
public class ThreadDemo5 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) ; //该行代码主要是让子线程跑一会儿, 以便主线程获取到子线程的 RUNNABLE 方法 }); System.out.println(t1.getState()); //NEW t1.start(); System.out.println(t1.getState()); //RUNNABLE Thread.sleep(3000); System.out.println(t1.getState()); //TERMINATED } }
NEW: 创建了 Thread 对象, 但是还没调用 start() 方法 (内核中还没创建对应的 PCB)
TERMINATED: 内核中PCB已经执行完毕了, 但是 Thread 对象还在 (执行过 start(), 并且 run() 方法执行完毕)
RUNNABLE: 可运行的 (包含两类线程)
- 正在CPU上运行的线程
- 在就绪队列中, 随时可以去CPU上运行的线程
WAITING: wait / join /IO … 产生的阻塞
TIMED_WAITING: sleep() 产生的阻塞
BLOCKED: 等待锁产生的阻塞
线程的状态转换
线程中断
线程中断不是直接终止中断, 而是通知线程, 你应该要中止了, 具体线程中止不中止, 还是看代码里面的具体逻辑
下面介绍两种线程中断的方式 (其实本质上是一种, 都是通过标志位来通知线程中断)
线程中断的实现 – 自写一个标志位控制线程中断
// 使用标志位来控制线程中断 (只是通知, 不是强制中断) public class Main{ public static boolean f = true; //自写的标志位 public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { int n = 0; while(f) { //主线程修改标志位, 子线程就能读取到, 以此来判断子线程是否要中断 System.out.println("hello " + n); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } n++; } }); t.start(); Thread.sleep(3000); System.out.println(t.getState()); f = false; System.out.println(t.getState()); } }
线程中断的实现 – 通过 Thread 自带的标志位来进行线程中断
// 使用 Thread 自带的标志位来控制线程中断 interrupted public class ThreadDemo3 { public static void Main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { int x = 0; while(!Thread.currentThread().isInterrupted()) { // Thread.currentThread() -> 来获取当前线程 // Thread.currentThread().isInterrupted() -> 判断当前线程是否被中断, 被中断返回true // 那 !Thread.currentThread().isInterrupted() -> 就是如果线程被中断, 返回 false, 即退出 while 循环 try { System.out.println("hello " + x++); //懒人写法,测试用的, 正经写代码别这么写(年终奖警告!!) Thread.sleep(500); } catch (InterruptedException e) { // interrupt会触发 sleep 内部的异常, 因此可以在这里面进行线程被 interrupted 之后的事情 e.printStackTrace(); System.out.println("中断执行, sleep 被唤醒, 进入异常中 ... "); break; } } }); t.start(); Thread.sleep(3000); t.interrupt(); // interrupted 做的事 // 1.将线程内部的标志位置 true // 2.在 sleep 过程中被调用的话, interrupt会触发 sleep 内部的异常, 导致 sleep 提前退出 // sleep 唤醒后做的事 // 1.执行异常处理 // 2.sleep唤醒后会清空标志位 (置 false) }
下面是上述代码的重点, 单拎出来以示尊重~~
interrupt() 做的事:
1.将线程内部的标志位置 true
2.在 sleep 过程中被调用的话, interrupt会触发 sleep 内部的异常, 导致 sleep 提前退出
sleep 唤醒后做的事:
1.执行异常处理
2.sleep唤醒后会清空标志位 (置 false)