【java并发编程】yeild(),sleep(),wait()区别详解

本文详细介绍了Java中线程控制的各种方法,包括sleep(), join(), yield(), wait(), notify(), notifyAll()等方法的作用及使用场景。同时阐述了run()和start()的区别,以及synchronized关键字的用途。

本文转载至http://dylanxu.iteye.com/blog/1322066 并附加线程阻塞的补充

1、sleep()

使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。

例 如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有 Sleep()方法,

只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。

总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。

 

2、join()

join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。

 

3、yield()

该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。

 

4、wait()和notify()、notifyAll()

 

这 三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,

阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线

程也有机会访问共享数据 呢?此时就用这三个方法来灵活控制。

wait() 方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调

用notify()方法 后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如

果锁标志等待池中没有线程,则notify()不起作用。

notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。

注意 这三个方法都是java.lang.Object的方法。

 

 

二、run()和start()

把需要处理的代码放到run()方法中,start()方法启动线程将自动调用run()方法,这个由java的内存机制规定的。并且run()方法必需是

public访问权限,返回值类型为void。

 

三、关键字synchronized

该关键字用于保护共享数据,当然前提条件是要分清哪些数据是共享数据。每个对象都有一个锁标志,当一个线程访问到该对象,

被Synchronized修饰的数据将被"上锁",阻止其他线程访问。当前线程访问完这部分数据后释放锁标志,其他线程就可以访问了。

 

 

四、wait()和notify(),notifyAll()是Object类的方法,sleep()和yield()是Thread类的方法。

(1)、常用的wait方法有wait()和wait(long timeout);

void wait() 在其他线程调用此对象的notify() 方法或者notifyAll()方法前,导致当前线程等待。

void wait(long timeout)在其他线程调用此对象的notify() 方法 或者 notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。

wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其他shnchronized数据可被别的线程使用。

 

wait() 和notify()因为会对对象的“锁标志”进行操作,所以他们必需在Synchronized函数或者 synchronized block中进行调用。如果在

non-synchronized函数或 non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异

常。。

 

(2)、Thread.sleep(long millis)必须带有一个时间参数。

sleep(long)使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;

sleep(long)可使优先级低的线程得到执行的机会,当然也可以让同优先级的线程有执行的机会;

sleep(long)是不会释放锁标志的。

 

(3)、yield()没有参数

sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,yield方法使当前线程让出

CPU占有权,但让出的时间是不可设定的。

yield()也不会释放锁标志。

实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线

程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。

 

sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级

的线程此时获取CPU占有权。 在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先

级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。

 

yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只

能使同优先级的线程有执行的机会。



线程阻塞

    线程可以阻塞于四种状态:

    1、当线程执行Thread.sleep()时,它一直阻塞到指定的毫秒时间之后,或者阻塞被另一个线程打断;

    2、当线程碰到一条wait()语句时,它会一直阻塞到接到通知(notify())、被中断或经过了指定毫秒时间为止(若制定了超时值的话)

    3、线程阻塞与不同I/O的方式有多种。常见的一种方式是InputStream的read()方法,该方法一直阻塞到从流中读取一个字节的数据为止,它可以无限阻塞,因此不能指定超时时间;

    4、线程也可以阻塞等待获取某个对象锁的排他性访问权限(即等待获得synchronized语句必须的锁时阻塞)。

    注意,并非所有的阻塞状态都是可中断的,以上阻塞状态的前两种可以被中断,后两种不会对中断做出反应



### 关于 `Thread.yield()` 和 `Thread.sleep(0)` 的行为 #### 1. **`Thread.yield()` 方法** `Thread.yield()` 是一种提示机制,用于告诉调度器当前线程愿意让出 CPU 控制权给其他具有相同优先级的线程运行[^6]。需要注意的是,这只是一个建议而非强制指令,具体的行为取决于底层操作系统和 JVM 实现。 - 如果没有其他同优先级的线程准备好运行,则调用 `yield()` 后当前线程可能立即重新获得 CPU 控制权并继续执行。 - 它不会使线程进入阻塞状态,也不会释放锁资源。 #### 2. **`Thread.sleep(long millis)` 方法** `Thread.sleep(millis)` 让当前线程暂停指定毫秒数,在这段时间内放弃对 CPU 的占用[^7]。即使有更高优先级的线程存在,睡眠中的线程也无法被抢占提前恢复执行。 对于特殊情况下的参数设置为零 (`Thread.sleep(0)`): - 当传递时间为零时,实际上它会触发一次上下文切换过程,类似于 `yield()` 功能[^8]。然而两者之间仍存在一定差异: - `sleep(0)` 更加正式地通知系统希望发生短暂延迟以便其他任务有机会介入; - 而单纯依靠 `yield()` 则更多依赖于特定平台实现细节来决定效果如何。 以下是展示这两种方式简单使用的代码片段: ```java public class YieldSleepExample { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println("T1 started"); try { // Sleep with zero milliseconds or use yield() Thread.sleep(0); // Alternatively uncomment below line instead above one. // Thread.yield(); System.out.println("T1 after yielding/sleeping."); } catch (InterruptedException e) {} }); Thread t2 = new Thread(() -> System.out.println("T2 running")); t1.start(); t2.start(); t1.join(); // Wait until T1 finishes its execution before continuing further here... t2.join(); // Similarly wait till completion of second thread too.. System.out.println("Main completed all threads operations."); } } ``` 此程序定义两个独立工作的子进程分别打印消息到控制台;通过调整其中某个函数(要么是 `sleep(0)` 或者就是单纯的 `yield()`),可以观察不同情况下输出顺序的变化情况。 ### 总结区别要点 | 特性 | `Thread.yield()` | `Thread.sleep(0)` | |--------------------|-------------------------------------------|---------------------------------------| | 主要作用 | 提供机会给同等优先级其它线程 | 强制进行轻微延时 | | 是否涉及时间单位 | 不含任何时间概念 | 需传入整数值作为参数 | | 锁定状态的影响 | 不会影响已持有的同步对象锁定 | 类似不影响持有锁 | 尽管如此,在现代多核处理器环境下以及大多数实际应用场景里,直接显式调用这两个方法并不常见,因为更高级别的并发工具库提供了更为强大灵活的功能选项可供选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值