通过jstack命令查看线程的生命周期

注:数字顺序不代表先后顺序

阻塞

三种,同步阻塞,等待阻塞,其他阻塞。
同步阻塞(Entry Set),等待获取对象锁的同步队列,有些地方也叫锁池(每个对象都有一个内部锁,monitor监视器)。当某个线程访问一个对象中的Synchronize同步代码块时,如果发现该对象的监视器被别的线程持有,就进入该对象的 Entry Set,当前线程状态变成 BLOCKED
等待阻塞(Wait Set),占有某个对象监视器的线程,调用该对象的 wait() 方法放弃监视器,就进入了该对象的 Wait Set,当前线程状态变成 WAITING(on object monitor)
其他阻塞,调用sleep等方法进入阻塞状态,让出CPU,但不会放弃锁,当前线程进入 TIME_WAITING(sleeping) 状态。

通过 jstack 命令查看线程状态变化 

public class ObjectWaitTest {

    synchronized void objectWait() throws InterruptedException{
        System.out.println(Thread.currentThread().getName() + "进来先休息一下");
        Thread.sleep(10000);
        System.out.println(Thread.currentThread().getName() + "休息完了放弃锁");
        this.wait();
        System.out.println(Thread.currentThread().getName() + "获得锁");
        Thread.sleep(5000);
    }

    synchronized void objectNotify() throws InterruptedException {
        this.notifyAll();
        System.out.println(Thread.currentThread().getName()
                       +"唤醒所有线程进入锁池并选择一个获取锁");
        Thread.sleep(10000);
     }

    public static void main(String[] args) throws InterruptedException {
        ObjectWaitTest test = new ObjectWaitTest();
        for (int i = 0 ; i < 3 ; i ++){
            new Thread(()->
            {
                try {
                    System.out.println(Thread.currentThread().getName()+"调用同步块");
                    test.objectWait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        Thread.sleep(40000); //保证3个线程代码都执行了 wait方法
        test.objectNotify();
    }

}

执行上面的 main 函数,依次记录状态如下:

  • 0,1,2 三个线程依次调用同步块,0线程最先获取 test 对象的锁,1、2线程进入同步阻塞。

 

此时执行 jstack 命令,可以看到,0线程先占有了 test 对象的监视器,锁住了<0x00000000d675a770>地址,进入了代码块,执行过程中,调用了 sleep() 方法,状态变成了 TIMED_WAITING。同一时间,1、2线程由于获取不到 test 对象的监视器,进入了 test 对象的 Entry Set(waiting for monitor entry),一直等待给<0x00000000d675a770>地址上锁,状态变成 BLOCKED。3个线程都是对同一个地址的竞争,从这个地址后面括号里的描述也可以看出,就是和 test 对象相关的。如果这里使用 Lock 上锁的话,线程状态是 WAITING(parking)。


  • 0线程 sleep 完了,调用了 wait() 方法放弃了 test 对象的监视器,进入等待阻塞;接着,2线程获得了 test 对象的监视器。 上一步中,2线程是比1线程 后进入阻塞队列的,也就是说,谁能在下一次获取对象的锁,不是由进入 Entry Set 的顺序决定的,还和其他因素有关,可以暂时理解为随机。对应线程图中的6。 

通过 jstack 命令查看,in Object.wait(),0线程 调用了 test.wait(),线程状态切换成 WAITING,而2线程的状态 和 上一步的 0线程的一样,1线程依旧在Entry Set中。


  • 3个线程都执行了 wait() 方法,都进入了 Wait Set,线程状态WAITING(on object monitor)。如果这里调用的是 Condition 的 wait() 方法的话,线程状态就是 WAITING(parking)

从线程 dump 文件中可以看出,全部变成了 WAITING 状态。图中箭头方向是程序运行的时间方向。比如2线程,首先执行了 run 方法,然后获得了对象锁,进入了同步方法 objectWait,接着调用 wait() 方法,进入等待阻塞队列。


  • 主线程调用 test.notifyAll() ,唤醒所有调用过 test.wait() 进入等待阻塞的线程。执行对象的 nofity()/notifAll() 方法,必须要等到 synchronize 同步代码执行完毕才能获得锁。唤醒方法中特意加了一个 Thread.sleep(10000),就是为了测试这个。4图可以看出执行了 notifyAll() 方法,对应的 D图中,线程都变成了BLOCKED,说明它们被唤醒去竞争 test 对象锁了,但是锁仍然被主线程占据了,因为它还在 sleep,没有退出同步代码,所以线程们都被阻塞了。


  • 线程被唤醒后,只能有一个线程获取对象锁,所以又要竞争了,竞争成功变成就绪态,失败的仍然留在Wait Set,状态变成 BLOCKED。  

从线程dump文件中可以看出,1线程获得了锁,但是执行了 sleep() 变成了 TIME_WAITING状态,说明是一个有时间期限的等待,waiting on condition 等待一个条件让它退出阻塞状态,这个条件就是时间到期。0和2线程是阻塞状态,但是从 in Object wait() 可以看出,它们还在 Wait Set 中。

  • 程序执行完毕 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值