Java线程阻塞

Java线程阻塞

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一

定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

  1. sleep() 方法:sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间

内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。

  典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后

重新测试,直到条件满足为止。

  2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会

自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和

resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个

线程产生了结果后,调用 resume() 使其恢复。

  3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于

可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足

够的时间从而转到另一个线程。

  4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式

,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或

者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

  初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区

别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则

相反。

  上述的核心区别导致了一系列的细节上的区别。

  首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,

所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞

时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象

上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随

机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

  其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块

中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的

道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调

用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若

不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。

  wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和

操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作

系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语

(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间

通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

  关于 wait() 和 notify() 方法最后再说明两点:

  第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随

机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问

题。

  第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用

notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有

获得锁的那一个线程才能进入可执行状态。

  谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait()

方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小

心地避免死锁。

  以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify()

方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用

中我们应该灵活使用各种方法,以便更好地达到我们的目的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/100364.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 跟着实例学习ZooKeeper的用法: 计数器[通俗易懂]

    跟着实例学习ZooKeeper的用法: 计数器

    2022年3月3日
    184
  • java voliate关键字_java中voliate关键字有什么用?

    java voliate关键字_java中voliate关键字有什么用?1、保证内存可见性内存可见性,即线程A对volatile变量的修改,其他线程获取的volatile变量都是最新的。说到内存可见性就必须要提到Java的内存模型,如下图所示:如上图所示,所有线程的共享变量都存储在主内存中,每一个线程都有一个独有的工作内存,每个线程不直接操作在主内存中的变量,而是将主内存上变量的副本放进自己的工作内存中,只操作工作内存中的数据。当修改完毕后,再把修改后的结果放回到主内…

    2022年5月1日
    46
  • RabbitVCS无法启动,右键失效解决方案

    RabbitVCS无法启动,右键失效解决方案本来用着好好的RabbitVCS,突然间就无法使用了;重新安装也无效,一旦重启就会出现如下错误:原因是RabbitVCS依赖python2.7,但是我的系统默认的python版本,已经改为python3.5了,所以才导致了这个错误,只要把python的版本改回python2.7就可以解决这个问题了:把python2.7设置为更高的权限即可。如下,再启动就OK…

    2022年7月18日
    16
  • 解决Destroying ProtocolHandler [“ajp-apr-8009“]

    解决Destroying ProtocolHandler [“ajp-apr-8009“]今天刚开始调JSP网站,还好好的,后来中间注销了电脑一次,再打开调试它,就遇到了这个无法启动服务器的问题:DestroyingProtocolHandler[“ajp-apr-8009”]上网百度了下,说是端口号被占用了。解决办法:1、查看端口号被占用情况:步骤:调出命令窗:开始->运行->cmd,然后输入命令:netstat-ano如图(记下来占用该

    2025年8月26日
    4
  • cmd ping大包命令_cmd中ping命令无效

    cmd ping大包命令_cmd中ping命令无效在cmd中用PING命令时,出现’Ping’不是内部或外部命令,也不是可运行的程序或批处理文件。先了解一下内容:1、可执行文件、命令文件和批处理文件以.exe或者.com或者.bat为扩展名的文件分别被称为可执行文件、命令文件和批处理文件。2、外部命令和内部命令DOS命令可以分为外部命令和内部命令,内部命令包含在一个名为command.com的文件,在系统启动时候驻留在内存中。外部命令是保存在c…

    2025年11月20日
    5
  • java voliate_voliate关键字及其示例

    java voliate_voliate关键字及其示例voliate关键字1使变量在线程间可见对于避免不可见性问题,Java还提供了一种弱形式的同步,即使用了volatile关键字。该关键字确保了对一个变量的更新对其他线程可见。当一个变量被声明为volatile时候,线程写入时候不会把值缓存在寄存器或者或者在其他地方,当线程读取的时候会从主内存重新获取最新值,而不是使用当前线程的拷贝内存变量值。volatile虽然提供了可见性保证,但是不能使用他来…

    2022年4月29日
    85

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号