0

0

Java 线程状态之 BLOCKED

高洛峰

高洛峰

发布时间:2016-11-22 16:20:27

|

2840人浏览过

|

来源于php中文网

原创

BLOCKED 状态的定义

前面已经说过 blocked(阻塞) 的简单定义为:

一个正在阻塞等待一个监视器锁的线程处于这一状态。(A thread that is blocked waiting for a monitor lock is in this state.)

更加详细的定义可以参考 Thread.State 中的 javadoc:

/**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

这句话很长,可以拆成两个简单句来理解。

A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method。

立即学习Java免费学习笔记(深入)”;

一个处于 blocked 状态的线程正在等待一个监视器锁以进入一个同步的块或方法。

A thread in the blocked state is waiting for a monitor lock to reenter a synchronized block/method after calling Object.wait。

一个处于 blocked 状态的线程正在等待一个监视器锁,在其调用 Object.wait 方法之后,以再次进入一个同步的块或方法。

进入(enter)同步块时阻塞

先说第一句,这个比较好理解。

监视器锁用于同步访问,以达到多线程间的互斥。所以一旦一个线程获取锁进入同步块,在其出来之前,如果其它线程想进入,就会因为获取不到锁而阻塞在同步块之外,这时的状态就是 BLOCKED。

注:这一状态的进入及解除都不受我们控制,当锁可用时,线程即从阻塞状态中恢复。

我们可以用一些代码来演示这一过程:

@Test
public void testBlocked() throws Exception {
    class Counter {
        int counter;
        public synchronized void increase() {
            counter++;
            try {
                Thread.sleep(30000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    Counter c = new Counter();
    
    Thread t1 = new Thread(new Runnable() {
        public void run() {
            c.increase();
        }
    }, "t1线程");
    t1.start();
    
    Thread t2 = new Thread(new Runnable() {
        public void run() {
            c.increase();
        }
    }, "t2线程");
    t2.start();
    
    Thread.sleep(100); // 确保 t2 run已经得到执行
    assertThat(t2.getState()).isEqualTo(Thread.State.BLOCKED);
}

以上定义了一个访问计数器 counter,有一个同步的 increase 方法。t1 线程先进入,然后在同步块里面睡觉,导致锁迟迟无法释放,t2 尝试执行同步方法时就因无法获取锁而被阻塞了。

VisualVM 监控显示了 t2 线程的状态:

55651933a6254.jpg

图上的“监视(monitor)”状态即为 BLOCKED 状态。可以看到在t1睡眠期间t2处于 BLOCKED 状态。

BLOCKED 状态可以视作是一种特殊的 WAITING,特指等待锁。

wait 之后重进入(reenter)同步块时阻塞

现在再次来看第二句:

2. A thread in the blocked state is waiting for a monitor lock to reenter a synchronized block/method after calling Object.wait。

一个处于 blocked 状态的线程正在等待一个监视器锁,在其调用 Object.wait 方法之后,以再次进入一个同步的块或方法。

这句话有点绕,也不好翻译成一句简洁的中文。如果没有对 wait 的相关背景有较好的理解,则不容易理解这句话。我们在此把它稍微展开讲一下。既然是 reenter,说明有两次 enter,这个过程是这样的:

凌夕卡密微商城
凌夕卡密微商城

卡密微商城支持三级分销,拥有唯一的推广链接,一次推广,三级奖励,可以查看下级会员统计信息,购买卡密之后自动显示卡密信息,无需等待,支持卡密充值、支付宝充值,可以在线提现。开发语言:JAVA开发框架:Struts2+Spring+Hibernate后台用到Freemarker模板引擎服务器环境:JDK1.7Tomcat7.0Mysql5.1演示网站用户名:admin,密码:222222安装教程:1、

下载

调用 wait 方法必须在同步块中,即是要先获取锁并进入同步块,这是第一次 enter。

而调用 wait 之后则会释放该锁,并进入此锁的等待队列(wait set)中。

当收到其它线程的 notify 或 notifyAll 通知之后,等待线程并不能立即恢复执行,因为停止的地方是在同步块内,而锁已经释放了,所以它要重新获取锁才能再次进入(reenter)同步块,然后从上次 wait 的地方恢复执行。这是第二次 enter,所以叫 reenter。

但锁并不会优先给它,该线程还是要与其它线程去竞争锁,这一过程跟 enter 的过程其实是一样的,因此也可能因为锁已经被其它线程据有而导致 BLOCKED。

这一过程就是所谓的 reenter a synchronized block/method after calling Object.wait。

我们也用一段代码来演示这一过程:

@Test
public void testReenterBlocked() throws Exception {
    class Account {
        int amount = 100; // 账户初始100元
        public synchronized void deposit(int cash) { // 存钱
            amount += cash;
            notify();
            try {
                Thread.sleep(30000); // 通知后却暂时不退出
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        public synchronized void withdraw(int cash) { // 取钱
            while (cash > amount) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            amount -= cash;
        }
    }
    Account account = new Account();
    
    Thread withdrawThread = new Thread(new Runnable() {
        public void run() {
            account.withdraw(200);
        }
    }, "取钱线程");
    withdrawThread.start();
    
    Thread.sleep(100); // 确保取钱线程已经得到执行
    
    assertThat(withdrawThread.getState()).isEqualTo(Thread.State.WAITING);
    
    Thread depositThread = new Thread(new Runnable() {
        public void run() {
            account.deposit(100);
        }
    }, "存钱线程");
    Thread.sleep(10000); // 让取钱线程等待一段时间
    depositThread.start();

    Thread.sleep(300); // 确保取钱线程已经被存钱线程所通知到

    assertThat(withdrawThread.getState()).isEqualTo(Thread.State.BLOCKED);
}

简要介绍一下以上代码场景:

有一个账户对象,有存钱(deposit)和取钱(withdraw)方法,初始金额100元。

取钱线程先启动,并进入(enter)同步块,试图取200元,发现钱不够,调用 wait,锁释放,线程挂起(WAITING 状态)。

10秒后存钱线程启动,存入钱并通知(notify)取钱线程,但之后继续在同步块中睡眠,导致锁没有释放。

取钱线程收到通知后,退出 WAITING 状态,但已经不持有锁,当试图重新进入(reenter)同步块以恢复执行时,因锁尚未被存钱线程释放,于是被阻塞(BLOCKED 状态)。

监控的显示:

55651933a6254.jpg

如图,取钱线程先是 WAITING,在收到通知因无法获取锁而阻塞(BLOCKED)。

总结

综合来看这两句话,两层意思,其实还是一个意思,简单地讲,就是enter,reenter 也还是 enter,概括地讲:

当因为获取不到锁而无法进入同步块时,线程处于 BLOCKED 状态。

如果有线程长时间处于 BLOCKED 状态,要考虑是否发生了死锁(deadlock)的状况。

BLOCKED 状态可以视作为一种特殊的 waiting,是传统 waiting 状态的一个细分:

QQ图片20161122092324.png

由于还没有讲到 WAITING 状态,而这里有涉及到了 wait 方法,所以上面对 wait 也稍微做了些分析,在下一章节,会更加详细的分析 WAITING 和 TIMED_WAITING 这两个状态。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
云朵浏览器入口合集
云朵浏览器入口合集

本专题整合了云朵浏览器入口合集,阅读专题下面的文章了解更多详细地址。

0

2026.01.20

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

20

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

62

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

87

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

39

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

10

2026.01.19

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.19

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

19

2026.01.19

微信聊天记录删除恢复导出教程汇总
微信聊天记录删除恢复导出教程汇总

本专题整合了微信聊天记录相关教程大全,阅读专题下面的文章了解更多详细内容。

160

2026.01.18

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.7万人学习

C# 教程
C# 教程

共94课时 | 7.1万人学习

Java 教程
Java 教程

共578课时 | 48.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号