Java 多线程 线程的五种状态,线程 Sleep, Wait, notify, notifyAll
一、先来看看Thread类里面都有哪几种状态,在Thread.class中可以找到这个枚举,它定义了线程的相关状态:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
具体解释请见源码,下面简单解释下Thread的五种状态什么时候出现:
- NEW 新建状态,线程创建且没有执行start方法时的状态
- RUNNABLE 可运行状态,线程已经启动,但是等待相应的资源(比如IO或者时间片切换)才能开始执行
- BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
- WAITING 等待状态,当调用
Object.wait或者Thread.join()且没有设置时间,在或者LockSupport.park时,都会进入等待状态。 - TIMED_WAITING 计时等待,当调用
Thread.sleep()或者Object.wait(xx)或者Thread.join(xx)或者LockSupport.parkNanos或者LockSupport.partUntil时,进入该状态 - TERMINATED 终止状态,线程中断或者运行结束的状态
二、Sleep 与 Wait 的区别
由于wait方法是在Object上的,而sleep方法是在Thread上,当调用Thread.sleep时,并不能改变对象的状态,因此也不会释放锁。请看下面代码结果:
package springBootExample.example.simpleApplication;
public class TestThread {
public static void main(String[] args) {
Room room = new Room();
Thread man = new Thread(room, "男人");
Thread female = new Thread(room, "女人");
System.out.println("After new but before start thread name = "+man.getName()+" state = "+man.getState());
// 此时的man和female处于NEW状态
man.start();
System.out.println("After start Thread name ="+man.getName()+" state = "+man.getState());
female.start();
// 此时的man和female处于Runnable状态,但是等待相应的资源(比如IO或者时间片切换)才能开始执行,谁先获得资源就可以执行
System.out.println("小姐已经接待完客人");
}
}
class Room implements Runnable {
public int count = 1;
@Override
public void run() {
while (count <= 20) {
// BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "去小姐的房间,小姐累计接待客人:" + count + "个.");
count++;
try {
Thread.currentThread().sleep(100);
// this.wait(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
结果:
After new but before start thread name = 男人 state = NEW
After start Thread name =男人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:1个.
小姐已经接待完客人
男人去小姐的房间,小姐累计接待客人:2个.
男人去小姐的房间,小姐累计接待客人:3个.
男人去小姐的房间,小姐累计接待客人:4个.
男人去小姐的房间,小姐累计接待客人:5个.
男人去小姐的房间,小姐累计接待客人:6个.
男人去小姐的房间,小姐累计接待客人:7个.
男人去小姐的房间,小姐累计接待客人:8个.
男人去小姐的房间,小姐累计接待客人:9个.
男人去小姐的房间,小姐累计接待客人:10个.
男人去小姐的房间,小姐累计接待客人:11个.
男人去小姐的房间,小姐累计接待客人:12个.
男人去小姐的房间,小姐累计接待客人:13个.
男人去小姐的房间,小姐累计接待客人:14个.
男人去小姐的房间,小姐累计接待客人:15个.
男人去小姐的房间,小姐累计接待客人:16个.
男人去小姐的房间,小姐累计接待客人:17个.
男人去小姐的房间,小姐累计接待客人:18个.
男人去小姐的房间,小姐累计接待客人:19个.
男人去小姐的房间,小姐累计接待客人:20个.
女人去小姐的房间,小姐累计接待客人:21个.
从上面的结果可以看出,NEW状态在新创建一个线程时呈现,RUNNABLE是在线程调用start()方法。因为线程获得资源就可以执行,在main()方法中新建一个线程man.start()执行,因此新线程获得资源就可以执行,从第4行结果看出。
注意看最后面有一个女人。这是因为synchronized的代码同步时在while循环里面,因此最后一次男人和女人都进入到了while里面,然后才开始等待相应的锁。这就导致第20次执行完轮到了女人。
当调用wait时:
@Override
public void run() { while (count <= 20) {
// BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
// System.out.println("Before synchronized thread name = "+Thread.currentThread().getName()+" state = "+Thread.currentThread().getState());
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "去小姐的房间,小姐累计接待客人:" + count + "个.");
count++;
try {
// Thread.currentThread().sleep(100);
this.wait(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:
After new but before start thread name = 男人 state = NEW
After start Thread name =男人 state = RUNNABLE
小姐已经接待完客人
男人去小姐的房间,小姐累计接待客人:1个.
女人去小姐的房间,小姐累计接待客人:2个.
男人去小姐的房间,小姐累计接待客人:3个.
女人去小姐的房间,小姐累计接待客人:4个.
8 女人去小姐的房间,小姐累计接待客人:5个.
男人去小姐的房间,小姐累计接待客人:6个.
女人去小姐的房间,小姐累计接待客人:7个.
男人去小姐的房间,小姐累计接待客人:8个.
男人去小姐的房间,小姐累计接待客人:9个.
女人去小姐的房间,小姐累计接待客人:10个.
男人去小姐的房间,小姐累计接待客人:11个.
女人去小姐的房间,小姐累计接待客人:12个.
男人去小姐的房间,小姐累计接待客人:13个.
女人去小姐的房间,小姐累计接待客人:14个.
男人去小姐的房间,小姐累计接待客人:15个.
女人去小姐的房间,小姐累计接待客人:16个.
男人去小姐的房间,小姐累计接待客人:17个.
女人去小姐的房间,小姐累计接待客人:18个.
男人去小姐的房间,小姐累计接待客人:19个.
女人去小姐的房间,小姐累计接待客人:20个.
但是如果稍作修改就会出现弄一种情况,代码如下:
class Room implements Runnable {
public int count = 1;
@Override
public void run() {
while (count <= 20) {
// BLOCKED 阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态
System.out.println("Before synchronized thread name = "+Thread.currentThread().getName()+" state = "+Thread.currentThread().getState());
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "去小姐的房间,小姐累计接待客人:" + count + "个.");
count++;
try {
Thread.currentThread().sleep(100);
// this.wait(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
结果:
After new but before start thread name = 男人 state = NEW
After start Thread name =男人 state = RUNNABLE
Before synchronized thread name = 男人 state = RUNNABLE
小姐已经接待完客人
男人去小姐的房间,小姐累计接待客人:1个.
Before synchronized thread name = 女人 state = RUNNABLE
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:2个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:3个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:4个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:5个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:6个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:7个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:8个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:9个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:10个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:11个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:12个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:13个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:14个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:15个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:16个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:17个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:18个.
Before synchronized thread name = 女人 state = RUNNABLE
男人去小姐的房间,小姐累计接待客人:19个.
Before synchronized thread name = 男人 state = RUNNABLE
女人去小姐的房间,小姐累计接待客人:20个.
男人去小姐的房间,小姐累计接待客人:21个.
目前这种现象暂时还不是特别清楚原理,但是当男人和女人都在while循环等待时,Thread.currentThread().getName() 会获取当前线程的名字,而在循环中再获取当前名字时会出现这种交替的情况?其实Room资源一直是男人拥有。
三、Wait(), Notify() , NotifyAll()的使用
wait、notify、notifyall这几个一般都一起使用。不过需要注意下面几个重要的点:
- 调用wait\notify\notifyall方法时,需要与锁或者synchronized搭配使用,不然会报错
java.lang.IllegalMonitorStateException,因为任何时刻,对象的控制权只能一个线程持有,因此调用wait等方法的时候,必须确保对其的控制权。 - 如果对简单的对象调用wait等方法,如果对他们进行赋值也会报错,因为赋值相当于修改的原有的对象,因此如果有修改需求可以外面包装一层。
- notify可以唤醒一个在该对象上等待的线程,notifyAll可以唤醒所有等待的线程。
- wait(xxx) 可以挂起线程,并释放对象的资源,等计时结束后自动恢复;wait()则必须要其他线程调用notify或者notifyAll才能唤醒。
package springBootExample.example.simpleApplication;
public class TestWaitAndNotify {
Call call = new Call(false);
class MaMa extends Thread {
public MaMa(String name) {
super(name);
}
@Override
public void run() {
synchronized (call) {
try {
call.wait(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
call.setFlag(true);
// call.notifyAll();
for (int i = 0; i < 3; i++) {
System.out.println("进来一个吧");
call.notify();
try {
call.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Customer extends Thread {
public Customer(String name) {
super(name);
}
@Override
public void run() {
synchronized (call) {
while (!call.isFlag()) {
System.out.println(Thread.currentThread().getName() + "等待王妈妈的呼唤");
try {
call.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "进入小姐的房间");
}
}
}
public static void main(String[] args) {
TestWaitAndNotify test = new TestWaitAndNotify();
MaMa teacher = test.new MaMa("王妈妈");
Customer stu1 = test.new Customer("小米");
Customer stu2 = test.new Customer("小百");
Customer stu3 = test.new Customer("小阿");
teacher.start();
stu1.start();
stu2.start();
stu3.start();
}
}
class Call {
private boolean flag = false;
public Call(boolean flag) {
this.flag = flag;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
上面代码中21,24行包含了notify() 和notifyAll()方法的,61行注意内部类实例时的方法。代码的运行结果也会不相同,notify()输出的结果为:
小米等待王妈妈的呼唤
小阿等待王妈妈的呼唤
小百等待王妈妈的呼唤
进来一个吧
小米进入小姐的房间
进来一个吧
小阿进入小姐的房间
进来一个吧
小百进入小姐的房间
notifyAll()输出的结果为:
小米等待王妈妈的呼唤
小阿等待王妈妈的呼唤
小百等待王妈妈的呼唤
小百进入小姐的房间
小阿进入小姐的房间
小米进入小姐的房间
Reference
[1] http://www.cnblogs.com/xing901022/p/7846809.html
Java 多线程 线程的五种状态,线程 Sleep, Wait, notify, notifyAll的更多相关文章
- java线程的五种状态
五种状态 开始状态(new) 就绪状态(runnable) 运行状态(running) 阻塞状态(blocked) 结束状态(dead) 状态变化 1.线程刚创建时,是new状态 2.线程调用了sta ...
- Java线程的五种状态详解
状态转换图 1.new状态:通过new关键字创建了Thread或其子类的对象 2.Runnable状态:即就绪状态.可从三种状态到达,new状态的Thread对象调用start()方法,Running ...
- Java 多线程同步的五种方法
一.引言 闲话不多说,进入正题. 二.为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常.举个例子 ...
- 细说进程五种状态的生老病死——双胞胎兄弟Java线程
java线程的五种状态其实要真正高清,只需要明白计算机操作系统中进程的知识,原理都是相同的. 系统根据PCB结构中的状态值控制进程. 单CPU系统中,任一时刻处于执行状态的进程只有一个. 进程的五种状 ...
- java核心知识点学习----并发和并行的区别,进程和线程的区别,如何创建线程和线程的四种状态,什么是线程计时器
多线程并发就像是内功,框架都像是外功,内功不足,外功也难得精要. 1.进程和线程的区别 一个程序至少有一个进程,一个进程至少有一个线程. 用工厂来比喻就是,一个工厂可以生产不同种类的产品,操作系统就是 ...
- 并发编程——Java线程的6种状态及切换
前言 本次主要分享一下Java线程的六种状态及其转换. 如果对于线程的创建方式不太了解,推荐观看并发编程--认识java里的线程 线程的状态及其转换 操作系统线程的五种状态 新建(NEW) 就绪(RU ...
- JAVA 线程的6种状态
JAVA线程的6种状态 线程状态(Thread.State).线程处于下列状态的其中之一. 一个线程在一个时刻只能有一个状态.这些状态是虚拟机线程状态,不能反应任何操作系统的线程状态. 通过Threa ...
- 在java中怎样实现多线程?线程的4种状态
一.在java中怎样实现多线程? extends Thread implement Runnable 方法一:继承 Thread 类,覆盖方法 run(),我们在创建的 Thread 类的子类中重写 ...
- java线程五种状态
java线程五种状态: 创建 -> 就绪 -> 运行 -> 销毁 创建 -> 就绪 -> 运行 -> 等待(缺少资源) -> 销毁 下图:各种状态转换
随机推荐
- Mac上csv导入mysql提示错误[Error Code] 1290 - The MySQL server is running with the --secure-file-priv option解决办法
1.进入mysql查看secure_file_prive的值 $mysql -u root -p mysql>SHOW VARIABLES LIKE "secure_file_priv ...
- 使用pyinotify实现加强版的linux tail -f 命令,并且对日志类型的文本进行单独优化着色显示。
tail -f命令不能自动切换切片文件,例如日志是每100M生成一个新文件,tail -f不能自动的切换文件,必须关闭然后重新运行tail -f 此篇使用pyinotify,检测文件更新,并实现tai ...
- python tornado异步性能测试
测试两个接口 # -*- coding:utf-8 -*- import time import tornado.web import tornado.gen import tornado.ioloo ...
- highcharts图表中级入门:非histock图表的highcharts图表如何让图表产生滚动条
最近highcharts图表讨论群里面很多朋友都在问如何让highcharts图表在X轴数据多的情况下产生滚动条的问题,其实之前有一个解决办法是将装载图表的div容器用css样式表弄一个滚动条出来.这 ...
- Tomcat------如何查看80端口是否被占用
1.Window + R,打开“运行”窗口,输入cmd 2.输入netstat -nao,回车,一般来说80端口会被PID为4的程序占用 3.启动任务管理器,点击“查看”->“选择列” 4.勾选 ...
- ios开发之--MJRefresh的简单使用
MJRefresh是MJ大神写的框架,很强大,好多外国开发者都在用! 具体方法如下: -(void)requestData { NSString *userIdStr = [NSString stri ...
- RPM常用命令解释
RPM软件包管理器,英文:RPM Package Manager(原Red Hat Package Manager,现在是一个递归缩写) -i安装rpm包 -u升级rpm包 -q查询已安装的软件信息 ...
- iOS开发-UIImageView的contentMode属性
UIImageView 的contentMode这个属性是用来设置图片的显示方式,如居中.居右,是否缩放等,有以下几个常量可供设定:UIViewContentModeScaleToFillUIView ...
- [Ubuntu] 如何在 Lubuntu 安装 python-spidermonkey
SpiderMonkey 是由 Mozilla 开发的 Javascript 引擎,它由 C/C++ 编写而成.Mozilla 在其多个产品中使用了该引擎,包括 Firefox 浏览器. python ...
- centos6.4安装 jupyter-notebook
自上次发布了文章后有些网友就说不能实现效果,根据自己的实验发现确实有此事,那是因为版本的变化问题.这次基于yum仓库里的jupyter notebook 5.0.0版本实现: 系统:最小化安装[习惯性 ...