并发编程之线程状态

线程状态

Java语言定义了6种线程状态,在任意一个时间点中,一个线程只能有且只有其中的一种状态,并且可以通过特定的方法在不同状态之间转换。这6种状态分别是:
1、新建(New):创建后尚未启动的线程处于这种状态。
2、运行(Runnable):包括线程状态中的Running 和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着操作系统为它分配执行时间。
3、无限期等待(Waiting ):处于这种状态的线程不会被分配处理器执行时间,它们要等待被其他线程显式唤醒。
4、限期等待(Timed Waiting ):处于这种状态的线程也不会被分配处理器执行时间,不过无须等待被其他线程显式唤醒,在一定时间之后它们会由系统自动唤醒。
5、阻塞(Blocked):线程被阻塞了,“ 阻塞状态”与“ 等待状态”的区别是“ 阻塞状态”在等待着获取到一个排它锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“ 等待状态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。
6、结束(Terminated):已终止线程的线程状态,线程已经结束执行

流程图如下:
在这里插入图片描述

常见等待和阻塞方法

Object::wait/notify/notifyAll

wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用。当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
notify/notifyAll() 被执行时候,会唤醒一个或多个正处于等待状态的线程,然后被唤醒的线程继续往下执行。

public class WaitNotify {
    public static void main(String[] args){
        Object o = new Object();
        new Thread(() -> {
            try {
                synchronized (o){
                    o.wait();
                    System.out.println("wait方法");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            synchronized (o){
                o.notify();
                System.out.println("notify方法");
            }
        }).start();
    }
}
执行结果:
notify方法
wait方法

wait/notify方法的调用必须处在该对象的锁(Monitor)中,在调用这些方法时首先需要获得该对象的锁。否则会抛出IllegalMonitorStateException异常。

为什么wait和notify要依赖synchronized?
notify和notifyAll有什么区别?

回答这两个问题先看下图wait和notify原理:
wait和notify是基于synchronized的基础上实现的,synchronized在字节码编译是对应Monitor.enter和Monitor.exit,当对象wait后,会放入等待队列,notify会取出队列第一个元素移入同步队列,此元素会尝试获取锁,获取到可继续执行。而notifyAll会将等待队列中所有元素移入到同步队列,这就是notify和notifyAll最大区别。
在这里插入图片描述

Thread:join

thread.join的含义是当前线程需要等待上一线程终止之后才从thread.join返回。简单来说,就是线程没有执行完之前,会一直阻塞在join方法处。

public class JoinDemo{
    public static void main(String[] args) throws InterruptedException {
        ThreadPrint t1 = new ThreadPrint("t1");
        ThreadPrint t2 = new ThreadPrint("t2");
        t1.start();
        /**
         程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕
         所以结果是t1线程执行完后,才到主线程执行,相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会
         */
        t1.join();
        t2.start();
    }
}
class ThreadPrint extends Thread{
    public ThreadPrint(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i = 0; i < 5; i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}
执行结果:
t1:0
t1:1
t1:2
t1:3
t1:4
t2:0
t2:1
t2:2
t2:3
t2:4

Thread:yield

yield()的作用是让步,让出CPU。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行。
大体意思一伙人在挤着上公交车,原来A已经上车准备刷卡了,A突然不想上车了,下来和大伙再挤着一次,这一次A可能挤上来,也可能其他人挤上来,大家个凭本事。可通过设置线程的setPriority属性,越大表示优先级约高。挤公交车力气越大,越有可能挤上来。

public class YieldDemo extends Thread{
    public YieldDemo(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("" + this.getName() + "-----" + i);
            // 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
            if (i == 2) {
                this.yield();
            }
        }
    }
    public static void main(String[] args){
        YieldDemo yieldDemo1 = new YieldDemo("小");
        YieldDemo yieldDemo2 = new YieldDemo("大");
        // 设置优先级:MIN_PRIORITY最低优先级1;NORM_PRIORITY普通优先级5;MAX_PRIORITY最高优先级10
        yieldDemo1.setPriority(Thread.MIN_PRIORITY);
        yieldDemo2.setPriority(Thread.MAX_PRIORITY);
        yieldDemo1.start();
        yieldDemo2.start();
    }
}
运行结果:
大-----1-----2-----3-----4-----5-----1-----2-----3-----4-----5

上面运行结果,yieldDemo2 在i为2时让出CPU,当由于yieldDemo2 的优先级最高,yieldDemo1任然抢不过,当然这不是肯定结果,多运行几次有小概率其他结果。

LockSupport::park/unpark

LockSupport类是Java6引入的一个类,提供了基本的线程同步原语,LockSupport实际上是调用了Unsafe类里的函数。
AQS中实现线程挂起的方法,就是park,对应唤醒就是unpark。

public class LockSupportDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            System.out.println("t1park前");
            LockSupport.park();
            System.out.println("t1park后");
        });
        t1.start();

        Thread t2 = new Thread(()->{
            LockSupport.unpark(t1);
            System.out.println("t2 unpark t1");
        });
        t2.start();
    }
}
运行结果:
t1park前
t2 unpark t1
t1park后
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值