博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程篇2:[- sleep、wait、notify、join、yield -]
阅读量:6581 次
发布时间:2019-06-24

本文共 8739 字,大约阅读时间需要 29 分钟。

前言:线程的五种状态

本文是线程篇的一个分支,主要结合我的理解,看一下sleep和wait以及线程的一些状态

网上的图看起来都有点丑,我自己画了一幅:

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.测试代码:
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秒

---->[运行结果]----------------------02:29:小汽车开始启动,在路上跑02:29:小汽车跑到终点02:31:救护车开始启动,在路上跑02:31:救护车跑到终点复制代码

二、线程内sleep以及synchronized

1.子线程加入休眠
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子线程的运行(休眠)

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

3.当加锁睡眠时

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

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线程执行完成,才能进行执行

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

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()获取车钥匙

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

3.唤醒等待线程

注意wait和notify需要使用同一个对象,否则然并卵

在Ambulance线程跑完后唤醒Car线程,然后Car获取所后会进入就绪态

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.结果分析

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

四、Thread#join()方法 和Thread#yield

1.普通代码
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.结果分析

26:28:小汽车开始启动,在路上跑26:28:main线程结束26:31:小汽车跑到终点复制代码

3.join方法的使用

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

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调度权,之后再一起抢夺。运行状态-->就绪状态

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 可执行态

转载地址:http://vssno.baihongyu.com/

你可能感兴趣的文章
Mac OSX 编译 LeanSDR
查看>>
Unit1 作业
查看>>
AngularJs-02-表达式和作用域
查看>>
在不改变链表的情况下从尾到头打印连表
查看>>
AfxExtractSubString 函数的相关问题
查看>>
Linux学习之CentOS(七)--CentOS下j2ee环境搭建
查看>>
pythopn time & datetime模块(时间)
查看>>
微软工程师升级打怪之路
查看>>
python函数之参数、参数解构
查看>>
Java集群优化——dubbo+zookeeper构建高可用分布式集群
查看>>
jquery日历选择插件(收集)
查看>>
漫谈液晶显示器的保养与维护
查看>>
HP-ux AIX Linux扩展lv的方法
查看>>
Linux用户管理(八)Shell编程基础
查看>>
ps -aux僵尸进程
查看>>
StringUtils方法全集介绍
查看>>
springmvc 配置详解
查看>>
性能调校
查看>>
VMware workstation虚拟网卡类型介绍
查看>>
HTML:target=_blank
查看>>