线程中的几个方法分析

wait和notify

wait()方法

wait()执行后,当前线程出让CPU,释放锁,此时当前线程不再继续往下执行。从监视资源的线程中随机选一个继续执行。上一个wait的线程想要继续执行,需要等到其他线程把它唤醒,并且释放锁之后才能继续执行。

// 方法注释中写的,需要这么用
synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
  }

notify()和notifyAll()方法

和wait()方法配合使用,表示当前线程即将释放锁,通知jvm可以唤醒其他正在等待状态的线程。必须在synchronized同步的场景下使用,否则会抛出IllegalMonitorStateException异常。

举个栗子:

public class NotifyTest extends Thread{
    private int number = 6;
    public byte res[];
    public NotifyTest(int number, byte a[]){
        this.number = number;
        res = a;
    }

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + "进入方法");
        synchronized (res){
            while(number -- > 0){
                System.out.println(Thread.currentThread().getName() + "进入循环");

                try {
                    res.notify();// 这里表示立即唤醒等待的线程
                    String name = Thread.currentThread().getName();
                    // 因为t1后者t3进来之后总是wait,需要其他线程唤醒它,所以例子可能会因为某个线程wait无法停止
                    if("t1".equals(name) || "t3".equals(name)){
                        System.out.println("我是"+name+"线程,不继续执行了");
                        res.wait();
                    }else {
                        for (int i = 0; i < 3; i++) {
                            try {
                                TimeUnit.SECONDS.sleep(1);
                                System.out.println(i);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                      // res.notifyAll(); 表示唤醒当前线程即将释放锁,让jvm唤醒所有竞争这个锁的线程
                    }

                    if("t1".equals(name) || "t3".equals(name)){
                        System.out.println("我是"+name+"线程,我又能执行了");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static void main(String[] args) {
        final byte a[] = {0};//以该对象为共享资源
        Thread t1 = new Thread(new NotifyTest((2),a),"t1");
        Thread t2 = new Thread(new NotifyTest((2),a),"t2");
        Thread t3 = new Thread(new NotifyTest((2),a),"t3");

        t1.start();
        t3.start();
        t2.start();


    }
}

一次运行结果:

/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ com.tomas.thread.stu01.NotifyTest
t3进入方法       // t3率先进入方法获得资源锁
t2进入方法       // t2进入等待
t1进入方法       // t1进入等待
t3进入循环       // t3拥有资源锁可以继续执行
我是t3线程,不继续执行了  // 执行到wait而暂停执行,释放锁,让t1和t2去竞争
t1进入循环              // t1竞争到资源继续执行
我是t1线程,不继续执行了   // 在这之前,t1执行了notify,此时t3已经被唤醒了,t1释放锁
t2进入循环              // t2和t3竞争,t2成功获得资源
0
1
2
t2进入循环
0
1
2                                               // t2执行的时候会执行notify,此时t1已经被唤醒了,t2此时执行结束
我是t1线程,我又能执行了  // t1和t3竞争,t1继续执行
t1进入循环
我是t1线程,不继续执行了
我是t3线程,我又能执行了
t3进入循环
我是t3线程,不继续执行了
我是t1线程,我又能执行了  // 最终t3还在等待

"t3" #16 prio=5 os_prio=31 tid=0x00007fd554071800 nid=0xa903 in Object.wait() [0x00007000101de000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076ac27350> (a [B)
        at java.lang.Object.wait(Object.java:502)
        at com.tomas.thread.stu01.NotifyTest.run(NotifyTest.java:29)
        - locked <0x000000076ac27350> (a [B)
        at java.lang.Thread.run(Thread.java:748)

总结

使用obj作为共享资源。在同步obj对象的场景下使用。当前线程在执行obj.wait()之后出让cup使用权限,当前线程停止执行,释放锁资源。这时需要其他线程执行obj.notify()方法唤醒wait的线程。等其他线程执行完或者释放锁之后,等待线程继续往下执行。

interrupt和sleep

interrupt()方法

使用interrupted信号量实现线程之间的通信。线程外部向线程内部发出一个中断请求的时候,线程内部会有一个响应,但是线程并不会中断,线程中断的控制权在自己手中。此时被中断线程可以选择继续执行。

sleep()方法

sleep方法执行之后,线程不会释放锁资源。正在sleep的线程被外部请求中断后,会抛出InterruptedException异常,此时interrupted信号量被清除,即线程继续执行。

换句话说,信号量为true的线程往下执行,遇到sleep,wait这样的方法时,会会抛出中断异常,然后interrupted被复位。

Thread.interrupted()

中断信号量复位。

t1.interrupt()

中断方法,设置t1线程的中断状态为true。

举个栗子:

public class InterruptTest extends Thread {

    public static void main(String[] args) {

        Thread t1 = new Thread(new InterruptTest());
        Thread t2 = new Thread(new InterruptTest());
        t1.start();
        //t2.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //t1.interrupt();

    }


    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "进入方法");
        synchronized (InterruptTest.class) {
            System.out.println("中断标志第一次打印" + currentThread().isInterrupted());

            currentThread().interrupt(); // 中断自己,设置为true
            System.out.println("中断标志第二次打印" + currentThread().isInterrupted());
            try {
                System.out.println(Thread.currentThread().getName() + "暂停一下");
                TimeUnit.SECONDS.sleep(10);
                System.out.println("------------这里肯定不会执行,因为走到异常了");
            } catch (InterruptedException e) {

                System.out.println("中断标志第三次打印" + currentThread().isInterrupted());
                System.out.println("模拟一下异常流程处理");
            }

            System.out.println("我还没结束,继续打印");
            // 再中断一下自己
            Thread.currentThread().interrupt();
            System.out.println("中断标志第四次打印" + currentThread().isInterrupted());

            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {

                System.out.println("会进异常");
                System.out.println("中断标志第五次打印" + currentThread().isInterrupted());
                System.out.println(Thread.currentThread().getName() + "==" + e.getMessage());
            }
            System.out.println("不浪了,执行最后一句,退出run方法");

        }
    }
}

执行结果:

Thread-1进入方法
中断标志第一次打印false
中断标志第二次打印true
Thread-1暂停一下
中断标志第三次打印false
模拟一下异常流程处理
我还没结束,继续打印
中断标志第四次打印true
会进异常
中断标志第五次打印false
Thread-1==sleep interrupted
不浪了,执行最后一句,退出run方法

总结:

interrupt为true的线程执行过程中遇到sleep方法总会抛出中断异常,然后复位信号量为false。本例验证了interrupt=true的线程执行sleep时会抛中断异常,也可以让线程先sleep,从外部调用中断方法进行中断,也会抛中断异常。可以认为中断标志为true的线程执行sleep会进异常流程;正在sleep的线程被外部执行中断方法,也会进入异常流程。相同的是,进入异常之后,中断状态会被清空,设置为false,如果此时是用中断状态控制流程处理,则会继续执行,就和没有中断过一样。

问题

  • 如何停掉一个线程?

    (1)stop方法是过期的,为什么不推荐使用。这种暴力停止的方式会导致数据丢失。因此,线程的中断应该交给被中断的线程自己去决定要不要中断。

    (2)使用thread.interrupt()方法。jvm底层通过信号量实现。当一个线程正在wait或者sleep的时候去中断,线程就会响应一个中断异常。此时线程并没有停止,此时线程可以继续往下执行。

    (3)使用Thread.interrupted()方法。这个方法是重制信号量。

  • Java线程有几种状态?操作系统有几种?

    Java有六种状态。

    new 、runnable 、waiting、timed_waiting、blocked、terminated

    操作系统有五种状态:

    就绪、阻塞、等待、运行、完成

  • 线程的生命周期和触发机制

    线程的生命周期就指线程从创建到销毁,这个过程中会经历多种状态,状态转换的条件就是触发机制。

  • 线程状态的转换


    image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。