线程中的几个方法分析

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341