线程通信的常用方法:join interrupt wait notify yeild interrupted isInterrupted

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 的一般方法
上一篇:MySQL多表查询,SQL,笛卡尔积等值连接自连接外连接,SQL99新特性,完整详细可收藏


下一篇:python os 模块简介