join() 的作用
让父线程等待子线程结束之后才能继续运行**。**
// 子线程
public static class Child extends Thread {
@Override
public void run() {
System.out.println("测试");
// ...
}
}
// 创建child对象,此时child表示的线程处于NEW状态
Child child = new Child();
// child表示的线程转换为RUNNABLE状态
child.start();
// 等待child线程运行完再继续运行
child.join();
System.out.println("一定会等child运行完毕 才执行");
线程终止 相关的
Thread.interrupt()
- 标记线程为终止状态,需要配合Thread.interrupt() 或 isInterrupted() 使用来终止线程。
最后总结,关于这三个方法,
- interrupt()是给线程设置中断标志;
- interrupted()是检测中断并清除中断状态;
- isInterrupted()只检测中断。
- 还有重要的一点就是interrupted()作用于当前线程,
- interrupt()和isInterrupted()作用于此线程,即代码中调用此方法的实例所代表的线程。
interrupt() 只是中断标志
public static class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("i="+(i+1));
}
}
}
MyThread thread=new MyThread();
thread.start();
thread.interrupt();
System.out.println("第一次调用thread.isInterrupted():"+thread.isInterrupted());
System.out.println("第二次调用thread.isInterrupted():"+thread.isInterrupted());
System.out.println("thread是否存活:"+thread.isAlive());
第一次调用thread.isInterrupted():true
i=1
i=2
i=3
第二次调用thread.isInterrupted():true
thread是否存活:false
可以看出调用interrupt()方法后,线程仍在继续运行,并未停止,
- 但已经给线程设置了中断标志,
- 两个isInterrupted()方法都会输出true,也说明isInterrupted()方法并不会清除中断状态。
interrupted 打印当前线程是否中断
thread.start();
thread.interrupt();
System.out.println("第一次调用thread.isInterrupted():" + thread.isInterrupted());
System.out.println("第二次调用thread.isInterrupted():" + thread.isInterrupted());
//测试interrupted()函数
System.out.println("第一次调用thread.interrupted():" + Thread.interrupted());
//System.out.println("第二次调用thread.interrupted():" + thread.interrupted()); 这种不行呀
System.out.println("thread是否存活:" + thread.isAlive());
i=1
第一次调用thread.isInterrupted():true
i=2
i=3
第二次调用thread.isInterrupted():true #返回true 不变
第一次调用thread.interrupted():false #返回false,再次调用依然返回false
thread是否存活:false
为什么后面两个interrupted方法输出的都是false,而不是预料中的一个true一个false?注意!!!这是一个坑!!!
- 上面说到,interrupted()方法测试的是当前线程是否被中断,当前线程!!!当前线程!!!这里当前线程是main线程,
- 而thread.interrupt()中断的是thread线程,这里的此线程就是thread线程。
- 所以当前线程main从未被中断过,尽管interrupted()方法是以thread.interrupted()的形式被调用,
- 但它检测的仍然是main线程而不是检测thread线程,所以thread.interrupted()在这里相当于main.interrupted()。
interrupted 是检测中断并清除中断状态
Thread.currentThread().interrupt();
System.out.println("第一次调用Thread.currentThread().interrupt():"
+ Thread.currentThread().isInterrupted());
System.out.println("第一次调用thread.interrupted():"
+ Thread.interrupted()); //Thread.currentThread()
System.out.println("第二次调用thread.interrupted():"
+ Thread.interrupted());
第一次调用Thread.currentThread().interrupt():true
第一次调用thread.interrupted():true
第二次调用thread.interrupted():false
interrupted()方法有检测中断并清除中断状态的作用,预料中的输出应该是true-true-false
interrupt 真正终止线程
实现调用interrupt()方法真正的终止线程,则可以在线程的run方法中做处理即可,比如直接跳出run()方法使线程结束
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("i="+(i+1));
//检测当前线程 是否中断
if(this.isInterrupted()){
System.out.println("通过this.isInterrupted()检测到中断");
System.out.println("第一个interrupted()"+this.interrupted());
System.out.println("第二个interrupted()"+this.interrupted());
break;
}
}
System.out.println("因为检测到中断,所以跳出循环,线程到这里结束,因为后面没有内容了");
}
}
MyThread2 myThread=new MyThread2();
myThread.start();
//设置 中断状态
myThread.interrupt();
//sleep等待一秒,等myThread运行完
Thread.sleep(1000);
System.out.println("myThread线程是否存活:"+myThread.isAlive());
i=1
通过this.isInterrupted()检测到中断
第一个interrupted()true
第二个interrupted()false
因为检测到中断,所以跳出循环,线程到这里结束,因为后面没有内容了
myThread线程是否存活:false
yeild 使用
MyThread2 yt1 = new MyThread2("张三");
MyThread2 yt2 = new MyThread2("李四");
yt1.start();
yt2.start();
public static class MyThread2 extends Thread {
public MyThread2(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i == 30) {
Thread.yield();
}
}
}
}
- 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
张三-----1
张三-----2
李四-----1
张三-----3 #改成了,张三为3时,重新抢:李四抢到了
李四-----2
李四-----3 #李四为3,重新抢,张三抢到了
张三-----4
李四-----4
张三-----5
李四-----5
多线程间的通信
线程间的通信指的是两个线程之间的交互,如启动、终止、等待/唤醒。
-
启动
启动一个线程的方法有多种,参考 Java 多线程实现章节,这里每一种实现方法都是由一个线程启动另外一个线程的方法。
-
终止
-
Thread.stop()
暴力停止一个线程,线程的执行会立即停止掉。
-
Thread.interrupt()
标记线程为终止状态,需要配合Thread.interrupt() 或 isInterrupted() 使用来终止线程。
-
-
等待/ 唤醒
-
wait()
一个线程需要等待另外一个线程执行完成,调用 wait() 可以使得当前线程进入到当前同步块 monitor 对象的等待唤醒队列。
-
notify() / notifyAll()
notify() 用于唤醒 monitor 等待唤醒队列中的一个线程;notifyAll() 用于唤醒 monitor 等待唤醒队列中的所有线程。
-
join()
等待调用线程执行完成,再继续往下执行。
-
yeild()
暂时释放自身资源给同优先级线程使用。
-
https://blog.csdn.net/asing1elife/article/details/82905577
wait() 和 notify()
wait() 和 notify() 必须在 synchronized 语句块中使用
- wait() 是强迫一个线程等待
- notify() 是通知一个线程继续运行
public class WaitTest extends Thread {
//自己
private final Object self;
//下一个对象
private final Object last;
//通过 构造 传递name,自己 和 下一个对象
public WaitTest(String name, Object self, Object last) {
super(name);
this.self = self;
this.last = last;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// 锁住下一个对象
synchronized (last) {
// 锁住当前对象
synchronized (self) {
//如果为A 就打印 第X 次运行
if (super.getName().equals("A")) {
System.out.println("第 " + (i + 1) + " 次运行!");
}
//在打印出 name
System.out.println(super.getName());
// 等待一轮循环结束后唤醒当前线程
self.notify();
}
try {
// 释放下一个线程的对象锁
last.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
Object c = new Object();
WaitTest waitA = new WaitTest("A", a, b);
WaitTest waitB = new WaitTest("B", b, c);
WaitTest waitC = new WaitTest("C", c, a);
waitA.start();
waitB.start();
waitC.start();
}
}
第 1 次运行!
A
B
C
第 2 次运行!
A
B
C
第 3 次运行!
A
B
C
- wait() 是针对已经获取对象锁的线程进行操作
- 当线程获取对象锁后,调用 wait() 主动释放对象锁,同时该线程休眠
- 直到其他线程调用 notify() 唤醒该线程,才继续获取对象锁,并执行
- 调用 notify() 唤醒线程并不是实时的,而是等相应的 synchronized 语句块执行结束,自动释放对象锁
- 再由 JVM 选取休眠的线程赋予对象锁,并执行,从而实现线程间同步、唤醒的操作
对比
wait() 和 sleep()对比
wait() 和 sleep() 都可以通过 interrupt() 打断线程的暂停状态,从而使线程立刻抛出 InterruptedException
- 通过 interrupt() 打断线程时,只需在 catch() 中捕获到异常即可安全结束线程
- InterruptedException 是线程内部抛出,并不是 interrupt() 抛出
- 当线程执行普通代码时调用 interrupt() 并不会抛出异常,只有当线程进入 wait() / sleep() / join() 后才会立即抛出
- wait() 和 sleep() 都可以暂停当前线程,其区别在于 wait() 在暂定的同时释放了对象锁
- sleep() 是 Thread 的静态方法,wait() 是 Object 的一般方法