线程的五种状态

线程从创建到销毁一般分为五种状态,如下图:

1) 新建

当用new关键字创建一个线程时,就是新建状态。

2) 就绪

调用了 start 方法之后,线程就进入了就绪阶段。此时,线程不会立即执行run方法,需要等待获取CPU资源。

3) 运行

当线程获得CPU时间片后,就会进入运行状态,开始执行run方法。

4) 阻塞

当遇到以下几种情况,线程会从运行状态进入到阻塞状态。

  • 调用sleep方法,使线程睡眠。
  • 调用wait方法,使线程进入等待。
  • 当线程去获取同步锁的时候,锁正在被其他线程持有。
  • 调用阻塞式IO方法时会导致线程阻塞。
  • 调用suspend方法,挂起线程,也会造成阻塞。

需要注意的是,阻塞状态只能进入就绪状态,不能直接进入运行状态。因为,从就绪状态到运行状态的切换是不受线程自己控制的,而是由线程调度器所决定。只有当线程获得了CPU时间片之后,才会进入运行状态。

5) 死亡

当run方法正常执行结束时,或者由于某种原因抛出异常都会使线程进入死亡状态。另外,直接调用stop方法也会停止线程。但是,此方法已经被弃用,不推荐使用。

线程常用方法

1)sleep

当调用 Thread.sleep(long millis) 睡眠方法时,就会使当前线程进入阻塞状态。millis参数指定了线程睡眠的时间,单位是毫秒。 当时间结束之后,线程会重新进入就绪状态。

注意,如果当前线程获得了一把同步锁,则 sleep方法阻塞期间,是不会释放锁的。

2) wait、notify和notifyAll

首先,它们都是Object类中的方法。需要配合 Synchronized关键字来使用。

调用线程的wait方法会使当前线程等待,直到其它线程调用此对象的notify/notifyAll方法。 如果,当前对象锁有N个线程在等待,则notify方法会随机唤醒其中一个线程,而notifyAll会唤醒对象锁中所有的线程。需要注意,唤醒时,不会立马释放锁,只有当前线程执行完之后,才会把锁释放。

另外,wait方法和sleep方法不同之处,在于sleep方法不会释放锁,而wait方法会释放锁。wait、notify的使用如下:

public class WaitTest {
private static Object obj = new Object(); public static void main(String[] args) throws InterruptedException {
ListAdd listAdd = new ListAdd(); Thread t1 = new Thread(() -> {
synchronized (obj){
try {
for (int i = 0; i < 10; i++) {
listAdd.add();
System.out.println("当前线程:"+Thread.currentThread().getName()+"添加了一个元素");
Thread.sleep(300);
if(listAdd.getSize() == 5){
System.out.println("发出通知");
obj.notify();
}
}
} catch(InterruptedException e){
e.printStackTrace();
}
}
}); Thread t2 = new Thread(() -> {
synchronized (obj){
try {
if(listAdd.getSize() != 5){
obj.wait();
}
} catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("线程:"+Thread.currentThread().getName()+"被通知.");
} }); t2.start();
Thread.sleep(1000);
t1.start();
}
} class ListAdd {
private static volatile List<String> list = new ArrayList<String>(); public void add() {
list.add("abc");
} public int getSize() {
return list.size();
}
}

以上,就是创建一个t2线程,判断list长度是否为5,不是的话,就一直阻塞。然后,另外一个t1线程不停的向list中添加元素,当元素长度为5的时候,就去唤醒阻塞中的t2线程。

然而,我们会发现,此时的t1线程会继续往下执行。直到方法执行完毕,才会把锁释放。t1线程去唤醒t2的时候,只是让t2具有参与锁竞争的资格。只有t2真正获得了锁之后才会继续往下执行。

3) join

当线程调用另外一个线程的join方法时,当前线程就会进入阻塞状态。直到另外一个线程执行完毕,当前线程才会由阻塞状态转为就绪状态。

或许,你在面试中,会被问到,怎么才能保证t1,t2,t3线程按顺序执行呢。(因为,我们知道,正常情况下,调用start方法之后,是不能控制线程的执行顺序的)

咳咳,当前青涩的我,面试时就被问到这个问题,是一脸懵逼。其实,是非常简单的,用join方法就可以轻松实现:

public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MultiT("a"));
Thread t2 = new Thread(new MultiT("b"));
Thread t3 = new Thread(new MultiT("c")); t1.start();
t1.join(); t2.start();
t2.join(); t3.start();
t3.join();
} } class MultiT implements Runnable{
private String s;
private int i; public MultiT(String s){
this.s = s;
} @Override
public void run() {
while(i<10){
System.out.println(s+"===="+i++);
}
}
}

最终,我们会看到,线程会按照t1,t2,t3顺序执行。因为,主线程main总会等调用join方法的那个线程执行完之后,才会往下执行。

4) yield

Thread.yield 方法会使当前线程放弃CPU时间片,把执行机会让给相同或更高优先级的线程(yield英文意思就是屈服,放弃的意思嘛,可以理解为当前线程暂时屈服于别人了)。

注意,此时当前线程不会阻塞,只是进入了就绪状态,随时可以再次获得CPU时间片,从而进入运行状态。也就是说,其实yield方法,并不能保证,其它相同或更高优先级的线程一定会获得执行权,也有可能,再次被当前线程拿到执行权。

yield方法和sleep方法一样,也是不释放锁资源的。可以通过代码来验证这一点:

public class TestYield {
public static void main(String[] args) {
YieldThread yieldThread = new YieldThread();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(yieldThread);
t.start();
}
}
} class YieldThread implements Runnable { private int count = 0; @Override
public synchronized void run() {
for (int i = 0; i < 10; i++) {
count ++;
if(count == 1){
Thread.yield();
System.out.println("线程:"+Thread.currentThread().getName() + "让步");
}
System.out.println("线程:"+Thread.currentThread().getName() + ",count:"+count);
}
}
}

结果:

会看到,线程让步之后,并不会释放锁。因此,其它线程也没机会获得锁,只能把当前线程执行完之后,才会释放。(对于这一点,其实我是有疑问的。既然yield不释放锁,那为什么还要放弃执行权呢。就算放弃了执行权,别的线程也无法获得锁啊。)

所以,我的理解,yield一般用于不存在锁竞争的多线程环境中。如果当前线程执行的任务时间可能比较长,就可以选择用yield方法,暂时让出CPU执行权。让其它线程也有机会执行任务,而不至于让CPU资源一直消耗在当前线程。

5)suspend、resume

suspend 会使线程挂起,并且不会自动恢复,只有调用 resume 方法才能使线程进入就绪状态。注意,这两个方法由于有可能导致死锁,已经被废弃。

线程sleep,wait,notify,join,yield方法解析的更多相关文章

  1. Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别? 线程的sleep()方法和yield()方法有什么区别?

    Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别? sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间 ...

  2. java多线程中wait/notify/sleep/join/yield方法以及多线程的六种状态

    刚开始学线程的时候也是被这几个方法搞的云里雾里的,尤其是一开始看的毕老师的视频,老师一直在强调执行权和执行资格,看的有点懵逼,当然不是说毕老师讲的不好,就是自己有点没听明白,后来复习看了一些其他的博客 ...

  3. 线程的sleep()方法和yield()方法有什么区别?

    1.sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会 2.yield()方法只会给相同优先级或更高优先级的线程以运行的机会 3.线程执行sleep()方法后 ...

  4. 线程的sleep()方法和yield()方法区别

    1.sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会 2.yield()方法只会给相同优先级或更高优先级的线程以运行的机会 3.线程执行sleep()方法后 ...

  5. 线程的sleep()方法和yield()方法有什么区别?

    sleep()方法和yield()方法的区别: sleep()方法给其他线程运行机会时,不考虑线程的优先级,因此会给低优先级的线程运行机会:yield()方法只会给相同优先级或更高优先级的线程运行机会 ...

  6. 线程的 sleep()方法和 yield()方法有什么区别?

    ① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会:yield()方法只会给相同优先级或更高优先级的线程以运行的机会: ② 线程执行 sleep()方法后 ...

  7. Java并发编程原理与实战二十一:线程通信wait&notify&join

    wait和notify wait和notify可以实现线程之间的通信,当一个线程执行不满足条件时可以调用wait方法将线程置为等待状态,当另一个线程执行到等待线程可以执行的条件时,调用notify可以 ...

  8. Java线程和多线程(二)——对象中的wait,notify以及notifyAll方法

    Java对象中的wait,notify以及notifyAll方法 在Java的Object类中包含了3个final的方法,这三个方法允许线程来交流资源是否被锁定.这三个方法就是wait(),notif ...

  9. java ---线程wait/notify/sleep/yield/join

    一.线程的状态 Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态). New:新建状态,当线 ...

随机推荐

  1. regex(python)

    正则表达式 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/7/26 16:39 # @Author : jackendoff ...

  2. 897B. Chtholly's request#长度为偶数的回文数(模拟)

    题目出处:http://codeforces.com/problemset/problem/897/B 题目大意:构造一个题意要求的zcy数之后取模 #include<iostream> ...

  3. 直击LG曲面OLED首发现场,高端品质更出众

    简直是太棒了,我可以去看LG曲面OLED电视新品发布会了.这可是LG向中国首次推出的曲面OLED电视.在网上我就已经看到其实曲面OLED电视已经在韩国.美国还有欧洲都上市了,听说现在反响还挺不错.真没 ...

  4. bzoj1076 奖励关(概率dp)(状态压缩)

    BZOJ 1076 [SCOI2008]奖励关 Description 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须 ...

  5. King of the Waves

    You are organising a king of the hill tournament, the Buenos Aires Paddleboarding Competition (BAPC) ...

  6. 《C Prime Plus》第九节笔记

    第九节 函数 9.1 复习函数 函数原型 function prototype 函数调用 function call 函数定义 function definition 形参 实参 典型的ANSI C函 ...

  7. sqlserver中的数据导到mysql相关

    一.在sqlserver中生成数据表脚本,粘贴到记事本中,如下语法要进行替换 1.int IDENTITY (1, 1) NOT NULL——>id int unsigned NOT NULL ...

  8. 高可用(keepalived+lvs)

    博主本人平和谦逊,热爱学习,读者阅读过程中发现错误的地方,请帮忙指出,感激不尽 架构图: 本次实验严格按照下图完成 1.系统环境设置 1.1SELinux设置 vim /etc/selinux/con ...

  9. java和javac命令

    记录一下,今天无意中用到单独编译和执行某个java类,遇到各种Error: Could not find or load main class等问题,解决方案如下其中2和3选其一试试~ 1.javac ...

  10. Windows 10操作系统针对不同环境下的安装方法

    一.电脑系统能正常运行 1.解压win10镜像文件 到电脑的非系统分区,运行setup安装文件 2.点击setup应用程序,准备安装 3.准备安装 4.等待安装过程结束,重启即可. 二.光盘安装 1. ...