前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程篇2:[- sleep、wait、notify、join、yield -]

线程篇2:[- sleep、wait、notify、join、yield -]

作者头像
张风捷特烈
发布2020-04-30 15:30:13
3650
发布2020-04-30 15:30:13
举报
前言:线程的五种状态

本文是线程篇的一个分支,主要结合我的理解,看一下sleep和wait以及线程的一些状态 网上的图看起来都有点丑,我自己画了一幅:

线程状态
线程状态
代码语言:javascript
复制
1.New:       新建态:  new Thread ~ thread.start期间
2.Runnable:  可执行态: 可被CPU调度执行期间。
3.Running     运行态: 线程获取CPU权限进行执行
4.Blocked     阻塞状态: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。
    |---等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
    |---同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用)时
    |---其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时
5.Dead        死亡状态: 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

一、Thread.sleep简述

暂停当前线程,进入Blocked状态,把cpu片段让出给其他线程。

1.测试代码:
代码语言:javascript
复制
public class Main0 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");

    public static void main(String[] args) {
        new Thread(new Car()).start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Ambulance()).start();
    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车跑到终点");
        }
    }

    private static class Ambulance implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车开始启动,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车跑到终点");
        }
    }
}
复制代码

2. 结果分析:注02:29代表当前时刻的分秒,即2分29秒
代码语言:javascript
复制
---->[运行结果]----------------------
02:29:小汽车开始启动,在路上跑
02:29:小汽车跑到终点
02:31:救护车开始启动,在路上跑
02:31:救护车跑到终点

二、线程内sleep以及synchronized
1.子线程加入休眠
代码语言:javascript
复制
private static class Car implements Runnable {
    @Override
    public void run() {
        System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动,在路上跑");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车跑到终点");
    }
}
复制代码

2.结果分析

Car线程的任务是睡5s,可见主线程的休眠没有影响到Car子线程的运行(休眠)

代码语言:javascript
复制
18:48:小汽车开始启动,在路上跑
18:50:救护车开始启动,在路上跑
18:50:救护车跑到终点
18:53:小汽车跑到终点

3.当加锁睡眠时

在线程1中加synchronized(这里锁用sdf对象,你也可以任意)

代码语言:javascript
复制
public class Main2 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
    public static void main(String[] args) {
        new Thread(new Car()).start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Ambulance()).start();
    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            synchronized (sdf){
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动,在路上跑");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车跑到终点");
            }
        }
    }

    private static class Ambulance implements Runnable {
        @Override
        public void run() {
            synchronized (sdf){
                System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车开始启动,在路上跑");
                System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车跑到终点");
            }

        }
    }
}
复制代码

2.结果分析

Car线程和Ambulance线程在运行时使用同一把锁,线程在休眠时不会释放锁 所以Ambulance线程需要等待Car线程执行完成,才能进行执行

代码语言:javascript
复制
23:46:小汽车开始启动,在路上跑
23:51:小汽车跑到终点
23:51:救护车开始启动,在路上跑
23:51:救护车跑到终点

三、Object#wait()方法的作用

线程t1调用A对象wait()方法,会释放t1持有的锁,让t1进入等待队列(Blocked状态) 直到其他线程调用A对象的notify()方法notifyAll()方法t1进入同步队列(Blocked状态) 当t1获得锁后会进入就绪状态Runnable,获取CPU的调度权后会继续执行,再贴一遍这个图:

线程状态
线程状态

1.wait方法的使用

既然是释放当前线程的锁,那么不须有锁才行,而且必须用该锁的对象调用wait方法 比如上面是用sdf对象加锁的,必须使用sdf.wait();,否则会抛出InterruptedException

代码语言:javascript
复制
private static class Car implements Runnable {
    @Override
    public void run() {
        synchronized (sdf) {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动,在路上跑");
            try {
                Thread.sleep(3000);//模拟执行3s的任务之后
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            try {
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车紧急刹车....");
                sdf.wait();
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车跑到终点");
        }
    }
}
private static class Ambulance implements Runnable {
    @Override
    public void run() {
        synchronized (sdf) {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车开始启动,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车跑到终点");
        }
    }
}
复制代码

2.结果分析

在Car线程调用sdf.wait();后,锁将被释放,然后Ambulance线程就可以持有锁运行了 如果不唤醒线程,线程将一直阻塞,就是根本停不下来。打个比方就是sdf是司机 wait()之后就把车钥匙扔了,然后熄火了。钥匙拿不回来,车就跑不了。需要notify()获取车钥匙

代码语言:javascript
复制
30:48:小汽车开始启动,在路上跑
30:51:小汽车紧急刹车....
30:51:救护车开始启动,在路上跑
30:51:救护车跑到终点
然后就阻塞在这里停不下来了....

3.唤醒等待线程

注意wait和notify需要使用同一个对象,否则然并卵 在Ambulance线程跑完后唤醒Car线程,然后Car获取所后会进入就绪态

代码语言:javascript
复制
private static class Ambulance implements Runnable {
    @Override
    public void run() {
        synchronized (sdf) {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车开始启动,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救护车跑到终点");
            sdf.notify();
        }
    }
}
复制代码

4.结果分析
代码语言:javascript
复制
40:23:小汽车开始启动,在路上跑
40:26:小汽车紧急刹车....
40:26:救护车开始启动,在路上跑
40:26:救护车跑到终点
40:26:救护车:喂,哥们,醒醒,你可以开了...
40:26:小汽车开始启动....
40:31:小汽车跑到终点

四、Thread#join()方法 和Thread#yield
1.普通代码
代码语言:javascript
复制
public class Main6 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");

    public static void main(String[] args) {
        Thread car = new Thread(new Car());
        car.start();
        System.out.println(sdf.format(System.currentTimeMillis()) + ":main线程结束");

    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动,在路上跑");
            try {
                Thread.sleep(3000);//模拟执行3s的任务之后
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车跑到终点");
        }
    }
}
复制代码

2.结果分析
代码语言:javascript
复制
26:28:小汽车开始启动,在路上跑
26:28:main线程结束
26:31:小汽车跑到终点

3.join方法的使用

阻塞当前线程,知道join的线程结束或超时

代码语言:javascript
复制
public class Main6 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");

    public static void main(String[] args) {
        Thread car = new Thread(new Car());
        car.start();
        try {
            car.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sdf.format(System.currentTimeMillis()) + ":main线程结束");

    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车开始启动,在路上跑");
            try {
                Thread.sleep(3000);//模拟执行3s的任务之后
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽车跑到终点");
        }
    }
}
---->[打印结果]-----------------------------
31:53:小汽车开始启动,在路上跑
31:56:小汽车跑到终点
31:56:main线程结束
复制代码

4.yield方法测试

让出线程的cpu调度权,之后再一起抢夺。运行状态-->就绪状态

代码语言:javascript
复制
public class Main7 {
    public static void main(String[] args) {
        Thread car = new Car("car");
        Thread ambulance = new Car("Ambulance");
        car.start();
        ambulance.start();
    }

    private static class Car extends Thread {
        public Car(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(getName() + "----" + i);
                if (i == 5) {
                    yield();
                }
            }
        }
    }
}
复制代码

运行了几组数据,大部分满足让出调度权后,另一个线程执行,也有少量情况不是。 所以yield说明我现在不急,可以划划水,执行权可以让出去。不过接下来谁执行还是不定的。


五、小结
1.需要补充的点:

1.关于synchronized锁这里不展开 2.关于synchronized锁对象需要一致,否则锁不住,然并卵。常用class对象,如Car.class 3.可以设置超时唤醒xxx.wait(3000),即3s后唤醒线程。 4.可以设置join超时xxx.join(3000),即3s后线程进入就绪态。 5.notifyAll:来着黑暗寒冬的随从们,仆人们,士兵们,听从克尔苏加德的召唤吧!


2.简单比较

item

从属

是否释放锁

状态转变为

异常

sleep

Thread 静态方法

NO

阻塞

InterruptedException

wait

Object 公共方法

YES

等待队列

IllegalMonitorStateException+InterruptedException

notify/notifyAll

Object 公共方法

--

同步队列

IllegalMonitorStateException

join

Thread 公共方法

NO

阻塞

InterruptedException

yield

Thread 公共方法

NO

可执行态

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年05月06日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:线程的五种状态
  • 一、Thread.sleep简述
    • 1.测试代码:
      • 2. 结果分析:注02:29代表当前时刻的分秒,即2分29秒
      • 二、线程内sleep以及synchronized
        • 1.子线程加入休眠
          • 2.结果分析
            • 3.当加锁睡眠时
              • 2.结果分析
              • 三、Object#wait()方法的作用
                • 1.wait方法的使用
                  • 2.结果分析
                    • 3.唤醒等待线程
                      • 4.结果分析
                      • 四、Thread#join()方法 和Thread#yield
                        • 1.普通代码
                          • 2.结果分析
                            • 3.join方法的使用
                              • 4.yield方法测试
                              • 五、小结
                                • 1.需要补充的点:
                                  • 2.简单比较
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档