Java多线程 线程状态及转换 wait sleep yield join
线程的状态转化关系
(1). 新建状态(New):新创建了一个线程对象。
(2). 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
(3). 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
(4). 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
- 等待阻塞(WAITING):运行的线程执行wait()方法,JVM会把该线程放入等待池中。
- 同步阻塞(Blocked):运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
- 超时阻塞(TIME_WAITING):运行的线程执行sleep(long)或join(long)方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
(5). 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
下面的流程感觉有点问题,可以参考:
、线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但不管怎样,当我们new了这个对象后,线程就进入了初始状态; 、当该对象调用了start()方法,就进入可运行状态; 、进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态; 、进入运行状态后情况就比较复杂了 4.1、run()方法或main()方法结束后,线程就进入终止状态; 4.2、当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源)。当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片; 4.3、线程调用了yield()方法,意思是放弃当前获得的CPU时间片,回到可运行状态,这时与其他进程处于同等竞争状态,OS有可能会接着又让这个进程进入运行状态; 4.4、当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会立即进入锁池 状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入可运行状态,等待OS分配CPU时间片; 4.5、当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤 醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁 池,等待获取锁标记。 总算全部回忆了一遍JDK1.5在API的使用上有了较好的改进,效率得到很大的提高,不过几个状态转换的原理还是一样。
==========================================================================
Java中关于线程的几个方法:
1.sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是如果有Synchronized同步块,其他线程仍然不同访问共享数据。注意该方法要捕获异常
比如有两个线程同时执行(没有Synchronized),一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完成后,低优先级的线程才能执行;但当高优先级的线程sleep(5000)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
2.join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行。注意该方法也要捕获异常。
等待调用join方法的线程结束,再继续执行。如:在线程a中,用b线程对象调用join()函数b.join();则a线程会在此处等待b线程执行结束后在接着执行。//主要用于等待b线程运行结束,若无此句,a则会执行完毕,导致结果不可预测。
3.yield()
它与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
4.wait()和notify()、notifyAll()
这三个方法用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。前面说过Synchronized这个关键字用于保护共享数据,阻止其他线程对共享数据的存取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出Synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。当调用 notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有
锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
==============================================================
yield的例子:
public class Main{ public static void main(String[] args) { Thread producer = new Producer(); Thread consumer = new Consumer(); producer.setPriority(Thread.MIN_PRIORITY); //Min Priority consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority producer.start(); consumer.start(); } } class Producer extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("I am Producer : Produced Item " + i); Thread.yield(); } } } class Consumer extends Thread { public void run() { for (int i = 0; i < 5; i++) { System.out.println("I am Consumer : Consumed Item " + i); Thread.yield(); } } }
如果没有yield
I am Consumer : Consumed Item 0 I am Consumer : Consumed Item 1 I am Consumer : Consumed Item 2 I am Consumer : Consumed Item 3 I am Consumer : Consumed Item 4 I am Producer : Produced Item 0 I am Producer : Produced Item 1 I am Producer : Produced Item 2 I am Producer : Produced Item 3 I am Producer : Produced Item 4
有yield,是:
I am Consumer : Consumed Item 0 I am Producer : Produced Item 0 I am Producer : Produced Item 1 I am Consumer : Consumed Item 1 I am Consumer : Consumed Item 2 I am Consumer : Consumed Item 3 I am Producer : Produced Item 2 I am Producer : Produced Item 3 I am Producer : Produced Item 4 I am Consumer : Consumed Item 4
join的例子:
public class Main{ public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Test()); long start = System.currentTimeMillis(); System.out.println(System.currentTimeMillis()); t.start(); t.join(1000);//等待线程t 1000毫秒 System.out.println(System.currentTimeMillis()-start);//打印出时间间隔 System.out.println("Main finished");//打印主线程结束 } } class Test implements Runnable{ @Override public void run() { for (int i = 1; i <= 5; i++) { try { Thread.sleep(1000);//睡眠5秒,循环是为了方便输出信息 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("睡眠" + i); System.out.println(System.currentTimeMillis()); } System.out.println("TestJoin finished");//t线程结束 } }
1488349325998 1001 Main finished 睡眠1 1488349327000 睡眠2 1488349328000 睡眠3 1488349329000 睡眠4 1488349330000 睡眠5 1488349331000 TestJoin finished
Test类进行修改:
class Test implements Runnable{ @Override public void run() { synchronized (Thread.currentThread()) { for (int i = 1; i <= 5; i++) { try { Thread.sleep(1000);//睡眠5秒,循环是为了方便输出信息 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("睡眠" + i); System.out.println(System.currentTimeMillis()); } } System.out.println("TestJoin finished");//t线程结束 } }
1488349545595 睡眠1 1488349546597 睡眠2 1488349547597 睡眠3 1488349548597 睡眠4 1488349549597 睡眠5 1488349550597 5002 Main finished TestJoin finished
wait,sleep的区别
1)sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep 不会释放对象锁。
wait 是Object 类的方法,对此对象调用wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
1、这两个方法来自不同的类分别是Thread和Object
2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)
synchronized(x){ x.notify() //或者wait() }
4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
package com.qhong; import java.util.Date; public class Main { public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (Wait.class) { try { System.out.println(new Date() + " Thread1 is running"); Wait.class.wait(); System.out.println(new Date() + " Thread1 ended"); } catch (Exception ex) { ex.printStackTrace(); } } }); thread1.start(); Thread thread2 = new Thread(() -> { synchronized (Wait.class) { try { System.out.println(new Date() + " Thread2 is running"); Wait.class.notify(); // Don't use sleep method to avoid confusing for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } System.out.println(new Date() + " Thread2 release lock"); } catch (Exception ex) { ex.printStackTrace(); } } for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } System.out.println(new Date() + " Thread2 ended"); }); // Don't use sleep method to avoid confusing for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } thread2.start(); } } class Wait { }
Output:
Fri Mar 17 10:32:23 CST 2017 Thread1 is running Fri Mar 17 10:32:30 CST 2017 Thread2 is running Fri Mar 17 10:32:37 CST 2017 Thread2 release lock Fri Mar 17 10:32:37 CST 2017 Thread1 ended Fri Mar 17 10:32:43 CST 2017 Thread2 ended
hread1执行wait后,暂停执行
thread2执行notify后,thread1并没有继续执行,因为此时thread2尚未释放锁,thread1因为得不到锁而不能继续执行
thread2执行完synchronized语句块后释放锁,thread1得到通知并获得锁,进而继续执行
Sleep:
sleep方法告诉操作系统至少指定时间内不需为线程调度器为该线程分配执行时间片,并不释放锁(如果当前已经持有锁)。实际上,调用sleep方法时并不要求持有任何锁。
package com.qhong; import java.util.Date; public class Main { public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (Sleep.class) { try { System.out.println(new Date() + " Thread1 is running"); Thread.sleep(2000); System.out.println(new Date() + " Thread1 ended"); } catch (Exception ex) { ex.printStackTrace(); } } }); thread1.start(); Thread thread2 = new Thread(() -> { synchronized (Sleep.class) { try { System.out.println(new Date() + " Thread2 is running"); Thread.sleep(2000); System.out.println(new Date() + " Thread2 ended"); } catch (Exception ex) { ex.printStackTrace(); } } for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } }); // Don't use sleep method to avoid confusing for(long i = 0; i < 200000; i++) { for(long j = 0; j < 100000; j++) {} } thread2.start(); } } class Sleep { }
Output:
Fri Mar 17 10:38:44 CST 2017 Thread1 is running Fri Mar 17 10:38:46 CST 2017 Thread1 ended Fri Mar 17 10:38:51 CST 2017 Thread2 is running Fri Mar 17 10:38:53 CST 2017 Thread2 ended
http://blog.csdn.net/jiafu1115/article/details/6804386
http://blog.csdn.net/huang_xw/article/details/7316354
http://gityuan.com/2016/01/03/java-thread-wait-sleep/
http://www.cnblogs.com/DreamSea/archive/2012/01/16/2263844.html
http://lavasoft.blog.51cto.com/62575/99153/
http://dylanxu.iteye.com/blog/1322066
http://zheng12tian.iteye.com/blog/1233638
http://www.cnblogs.com/aboutblank/p/3631453.html
http://www.importnew.com/14958.html
https://software.intel.com/zh-cn/blogs/2011/12/16/java-sleepwait
http://www.jasongj.com/java/multi_thread/
http://blog.csdn.net/nyistzp/article/details/12878417
Java多线程 线程状态及转换 wait sleep yield join的更多相关文章
- Java多线程 - 线程状态
转自: http://www.cnblogs.com/lwbqqyumidi/p/3804883.html 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的 ...
- Java多线程:线程状态以及wait(), notify(), notifyAll()
一. 线程状态类型1. 新建状态(New):新创建了一个线程对象.2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运 ...
- java 多线程—— 线程让步
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java线程:线程状态的转换
Java线程:线程状态的转换 一.线程状态 线程的状态转换是线程控制的基础.线程状态总的可分为五大状态:分别是生.死.可运行.运行.等待/阻塞.用一个图来描述如下: 1.新状态:线程对象已 ...
- java线程状态及转换
java线程有6种状态: 新建线程new,启动线程runnable,阻塞block,限时等待timed_waiting,等待线程waiting,终止线程terminated 1.限时等待timed w ...
- java 多线程—— 线程等待与唤醒
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程--线程及相关的Java API
Java多线程--线程及相关的Java API 线程与进程 进程是线程的容器,程序是指令.数据的组织形式,进程是程序的实体. 一个进程中可以容纳若干个线程,线程是轻量级的进程,是程序执行的最小单位.我 ...
- Java线程状态及 wait、sleep、join、interrupt、yield等的区别
Java中的线程状态(详见Java线程状态及转换-MarchOn): wait:Object类的实例方法,释放CPU执行权,进入等待状态,直到 被中断.被拥有该对象锁的线程唤醒(notify或not ...
- Java多线程——线程的优先级和生命周期
Java多线程——线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...
随机推荐
- 【CSS系列】布局篇
一.让设计居中 1.使用自动空白边让设计居中 <style type="text/css"> body{ text-align:center; min-width:76 ...
- 运行npm install出现警告
如下: 解决: fsevent是mac osx系统的,你是在win或者Linux下使用了 所以会有警告,忽略即可
- hihocoder [Offer收割]编程练习赛14 可疑的记录
题目3 : 可疑的记录 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi有一棵N个节点的树,编号1-N,其中1号节点是整棵树的根.他把这棵树的N-1条边记录成N-1 ...
- exports和module.exports区别
参考:module.exports与exports的区别.关于exports的总结 exports 和 module.exports 的区别 module.exports是真正的模块接口,而expor ...
- thinkphp---手机访问切换模板!
手机访问切换模板:一般用在手机在做自适应的情况. 第一步:需要添加判断是否是手机访问的方法: http://www.cnblogs.com/e0yu/p/7561811.html 第二步:Home / ...
- Windows Bat 批处理脚本
Windows Bat 批处理脚本 – Getting Started – Variables – Return Codes – stdin, stdout, stderr – If/Then Con ...
- TC/IP协议簇
TCP/IP: 数据链路层:ARP,RARP网络层: IP,ICMP,IGMP传输层:TCP ,UDP,UGP应用层:Telnet,FTP,SMTP,SNMP. OSI:物理层:EIA/TIA-232 ...
- jquery ztree 刷新后记录折叠、展开状态
ztree :http://www.ztree.me/v3/main.php 项目中用到了这个插件,刚好也有需求 在页面刷新后,保存开始的展开.折叠状态, 其实 dtree: http://www.d ...
- Linux学习-->如何通过Shell脚本实现发送邮件通知功能?
1.安装和配置sendmail 不需要注册公网域名和MX记录(不需要架设公网邮件服务器),通过Linux系统自带的mail命令即可对公网邮箱发送邮件.不过mail命令是依赖sendmail的,所以我们 ...
- 【Python】sasa版:文件中csv读取在写入csv读取的数据和执行是否成功。
sasa写的文件(包含解析文字) # coding=utf- from selenium import webdriver from time import sleep import keyword ...