Object类:wait、notify、notifyAll
Thread类:sleep、join、yield
关于多线程有几个很常用的但是又很容易让人混淆的方法,今天专门对它们进行一番整理。
其中wait、notify、notifyAll设计为Object类中的方法是为了让任何对象都能被当成对象锁,也叫监视器。
Object#wait()
/**
* 如果没有其它线程调用对象锁的Object#notify()或
* 者Object#notifyAll()方法,那调用了同一个
* 对象锁的Object#wait()方法的线程会一直等待。
* 调用Object#wait()和调用Object#wait(0)效果是
* 一样的。
* 调用wait()方法必须先获得对象锁。wait()方法的
* 作用是释放当前拥有的对象锁并一直等待,直到有其
* 它线程使用同一个对象锁调用notify()方法
* 或notifyAll()方法才可能继续执行wait()
* 之后的代码。
* 有一种说法认为,执行wait()方法之后,可能会有中
* 断或者虚假的唤醒,所以wait()方法一般要放在一个
* 循环中(一般wait()方法的调用是伴随一个条件的,
* 条件发生时才调用wait(),
* 中断或者虚假唤醒可能发生在这个条件满足的情况
* 下,
* 所以如果中断或者虚假唤醒了,要重新检查这个条件
* 是否满足,满足的话就重新调用wait()):
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to
* //condition
* }
* 切记,wait()方法必须被拥有对象锁的线程调用。可
* 以查看notify()方法来了解线程获得对象锁的多种方
* 式。
*
* @throws IllegalMonitorStateException 调
* 用wait()方法的线程不是对象锁的持有者。
* @throws InterruptedException 线程在调用
* wait()方法之前或wait()方法等待中被其他线程中
* 断了,就会抛出这个异常并重置中断状态位。
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
public final void wait() throws InterruptedException {
wait(0);
}
wait()方法要和对象锁结合使用,调用wait()方法的前提是拥有对象锁。执行wait()方法后线程会一直等待,直到被中断或者被唤醒。wait()方法可能会抛出InterruptedException ,所以是可中断的方法。
Object#wait(timeout)
/**
* 如果没有其它线程调用对象锁的Object#notify()或
* 者Object#notifyAll()方法,那调用了同一个对象
* 锁的Object#wait(timeout)方法的线程会一直等待
* ;timeout长的时间过后同样会停止等待。
* 调用wait(timeout)方法的线程必须现获得对象锁。
* 这个方法会把调用它的线程放入对象锁的等待集合
* 中,并且唤醒在这个对象锁上等待的对象。线程会停
* 止执行余下的代码并陷入休眠。直到下面四个条件发
* 生其一:
* ①有线程调用了这个对象锁的notify()方法,并且
* 刚好是休眠线程被选中唤醒。
* ②有线程调用了这个对象锁的notifyAll()方法。
* ③有线程调用了休眠线程的interrupt()方法。
* ④过去了参数timeout长的时间。如果timeout是0,
* 那线程会一直等待,wait(0)和wait()效果一样。
* 上面四个条件有一个发生了,那休眠线程就会从对象
* 锁的等待集合中移去,它会和其它等待对象锁上的线
* 程一起竞争这个对象锁的使用权。一旦它获得了锁,
* 它就会被恢复为wait(timeout)前的状态,当过去
* timeout长的时间也一样。线程会继续执行
* wait(timeout)之后的代码。
*
* 除了被notify()/notifyAll()方法、中断和超时唤
* 醒以外,线程也会被所谓的虚假唤醒。虽然这种情况
* 在现实中很少发生,但是程序必须检查线程被唤醒的
* 条件是否满足,如果不满足就应该继续让程序休眠。
* 所以wait(timeout)方法最好在一个循环中被调用,
* 这样的话,线程被唤醒也可以检查条件是否满足:
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout);
* ... // Perform action appropriate to
* //condition
* }
* 如果线程被java.lang.Thread#interrupt()中断
* 之后调用wait(timeout)或者调用wait(timeout)
* 休眠中被java.lang.Thread#interrupt()中断,
* 就会抛出InterruptedException。这个异常在对
* 象锁被重置的时候抛出。
*
* wait方法只能用于解除当前对象锁;其它可以同步当
* 前线程的对象在线程休眠时依然可以处于锁定状态。
* 切记,wait(timeout)方法必须被拥有对象锁的线程
* 调用。可以查看notify()方法来了解线程获得对象
* 锁的多种方式。
*
* @param timeout 等待的最大时间数。
* @throws IllegalArgumentException 当
* timeout为负数。
* @throws IllegalMonitorStateException 当
* 调用wait(timeout)的线程不是对象锁的拥有者。
* @throws InterruptedException 如果线程被
* java.lang.Thread#interrupt()中断之后
* 调用wait(timeout)或者调用wait(timeout)休眠
* 中被java.lang.Thread#interrupt()中断抛出此
* 异常,并且会重置线程的中断状态。
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
public final native void wait(long timeout) throws InterruptedException;
wait(timeout)和wait的区别是多了一个超时时限,wait()的线程会一直等待,而线程在调用wait(timeout)方法timeout长的时间以后会自己解除休眠状态,wait(0)和wait()等效。wait(timeout)也抛出InterruptedException,所以也是可中断的方法。
Object#wait(timeout, nanos)
和wait(timeout)类似,只不过多了一个参数纳秒,wait(0,0)和wait(0)和wait()等效。也抛出InterruptedException,所以也是可中断的方法。
Object#notify()
/**
* 唤醒一个在对象锁上休眠的线程,如果有多个休眠的
* 线程,任意唤醒其中一个。在对象锁上休眠的线程指
* 的是调用了对象锁的wait方法(三个wait方法中的
* 任意一个)的线程。
* 被唤醒的线程只有在当前线程放弃对象锁之后才可能
* 继续执行,被唤醒的线程会和其它等待锁的线程(等
* 待执行synchronized)
* 一起竞争对象锁的拥有权,竞争是公平的,不会有特
* 权也不会有劣势。
* 此方法必须在拥有对象锁的前提下执行,线程获得对
* 象锁的方式有三种:
* ①执行对象锁的同步方法
* ②执行对象锁的同步代码块。
* ③如果对象锁是Class类型,执行此类的静态方法。
* 一个对象锁某一时刻只能被一个线程持有。
*
* @throws IllegalMonitorStateException 当
* 调用notify()方法的线程不是对象锁的拥有者。
* @see java.lang.Object#notifyAll()
* @see java.lang.Object#wait()
*/
public final native void notify();
Object#notifyAll()
/**
* 唤醒所有在对象锁上休眠的线程。在对象锁上休眠的线
* 程指的是调用了对象锁的wait方法(三个wait方法
* 中的任意一个)的线程。
* 被唤醒的线程只有在当前线程放弃对象锁之后才可能
* 继续执行,被唤醒的线程会和其它等待锁的线程(等
* 待执行synchronized)一起竞争对象锁的拥有权,
* 竞争是公平的,不会有特权也不会有劣势。
* 此方法必须在拥有对象锁的前提下执行,线程获得对
* 象锁的方式间Object#notify()方法。
*
* @throws IllegalMonitorStateException 当
* 调用notify()方法的线程不是对象锁的拥有者。
* @see java.lang.Object#notify()
* @see java.lang.Object#wait()
*/
public final native void notifyAll();
notify()和notifyAll()的区别是notify()唤醒对象锁上休眠线程中的一个,而notifyAll()唤醒所有休眠线程。被唤醒的线程想要继续执行代码必须获得锁,毕竟wait方法也是先拥有锁才能执行的。线程被唤醒之后和想获得锁进入同步代码的线程公平竞争锁的使用权。
一些代码示例:
public class WaitNotifyTest {
public static void main(String...args) throws InterruptedException {
Object o = new Object();
ET1 et1 = new ET1(o);
ET2 et2 = new ET2(o);
Thread t1 = new Thread(et1);
Thread t2 = new Thread(et2);
t1.start();
t2.start();
//为了让线程启动并执行
Thread.sleep(20000);
}
}
class ET1 implements Runnable{
/**
* 锁对象
*/
Object o;
public ET1(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET1获得锁...");
try {
//先不考虑虚假唤醒
o.wait();
} catch (InterruptedException e) {
System.out.println("ET1被中断...");
}
}
}
}
class ET2 implements Runnable{
/**
* 锁对象
*/
Object o;
public ET2(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET2获得锁...");
}
System.out.println("ET2执行完毕释放锁...");
}
}
执行结果:
ET2获得锁...
ET1获得锁...
ET2执行完毕释放锁...
不管t1和t2谁先获得锁,程序执行都不会完毕,没有线程notify t1,它会一直在等待。
public class WaitNotifyTest2{
public static void main(String...args) throws InterruptedException {
Object o = new Object();
ET1 et1 = new ET1(o);
ET2 et2 = new ET2(o);
Thread t1 = new Thread(et1);
Thread t2 = new Thread(et2);
t1.start();
t2.start();
//为了让线程启动并执行
Thread.sleep(20000);
}
}
class ET1 implements Runnable{
/**
* 锁对象
*/
Object o;
public ET1(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET1获得锁...");
try {
//先不考虑虚假唤醒
o.wait();
System.out.println("ET1 after wait...");
} catch (InterruptedException e) {
System.out.println("ET1被中断...");
}
}
System.out.println("ET1执行完毕释放锁...");
}
}
class ET2 implements Runnable{
/**
* 锁对象
*/
Object o;
public ET2(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET2获得锁...");
o.notifyAll();
}
System.out.println("ET2执行完毕释放锁...");
}
}
如果t1先获得锁:
ET1获得锁...
ET2获得锁...
ET2执行完毕释放锁...
ET1 after wait...
ET1执行完毕释放锁...
t1先获得锁,可以保证有t2去唤醒t1,t1和t2任务都会得到执行。
如果t2先获得锁:
ET2获得锁...
ET1获得锁...
ET2执行完毕释放锁...
t2先获得锁,那就没有线程去唤醒后得到锁的t1了,而休眠中的t1除非被中断,不然会一直休眠下面。
在t1被休眠时执行:t1.interrupt();则输出:
ET2获得锁...
ET1获得锁...
ET2执行完毕释放锁...
ET1被中断...
ET1执行完毕释放锁...
当然使用带参数的wait方法可以自己退出休眠:
public class WaitNotifyTest3{
public static void main(String...args) throws InterruptedException {
Object o = new Object();
ET1 et1 = new ET1(o);
ET2 et2 = new ET2(o);
Thread t1 = new Thread(et1);
Thread t2 = new Thread(et2);
t2.start();
t1.start();
//为了让线程启动并执行
Thread.sleep(20000);
/*t1.interrupt();
Thread.sleep(10000);*/
}
}
class ET1 implements Runnable{
/**
* 锁对象
*/
Object o;
public ET1(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET1获得锁...");
try {
//先不考虑虚假唤醒
//休眠5秒自己退出休眠
o.wait(5000);
System.out.println("ET1 after wait...");
} catch (InterruptedException e) {
System.out.println("ET1被中断...");
}
}
System.out.println("ET1执行完毕释放锁...");
}
}
class ET2 implements Runnable{
/**
* 锁对象
*/
Object o;
public ET2(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET2获得锁...");
//et2中没有调用notify方法
}
System.out.println("ET2执行完毕释放锁...");
}
}
输出结果:没有线程去唤醒ET1,ET1自己退出休眠。
ET1获得锁...
ET2获得锁...
ET2执行完毕释放锁...
ET1 after wait...
ET1执行完毕释放锁...
public class WaitNotifyTest4{
public static void main(String...args) throws InterruptedException {
Object o = new Object();
/*创建6个线程,1个ET1和5个ET2*/
ET1 et1 = new ET1(o);
Thread t1 = new Thread(et1);
Thread t2_1 = new Thread(new ET2(o,et1));
Thread t2_2 = new Thread(new ET2(o,et1));
Thread t2_3 = new Thread(new ET2(o,et1));
Thread t2_4 = new Thread(new ET2(o,et1));
Thread t2_5 = new Thread(new ET2(o,et1));
//先让t1执行
t1.start();
Thread.sleep(5000);
t2_1.start();t2_2.start();t2_3.start();t2_4.start();t2_5.start();
t1.interrupt();//中断t1
//五个线程中会有一个线程会先获得锁,这个线程执行完会唤醒wait的t1,让t1和剩下的四个线程一起竞争锁。
Thread.sleep(20000);
}
}
class ET1 implements Runnable{
//线程的休眠条件,只要condition为false,线程就要休眠
private boolean condition = false;
/**
* 锁对象
*/
Object o;
public ET1(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET1获得锁...");
try {
//防止虚假唤醒(虚假唤醒的时候condition不一定满足),所以重复检查条件
while(!condition) {
o.wait();
}
System.out.println("ET1 after wait...");
} catch (InterruptedException e) {
System.out.println("ET1被中断...");
//被中断也要检查条件,不满足也要继续休眠
while(!condition) {
try {
System.out.println("ET1被中断但是条件不满足,继续休眠...");
o.wait();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
System.out.println("ET1执行完毕释放锁...");
}
public boolean isCondition() {
return condition;
}
public void setCondition(boolean condition) {
this.condition = condition;
}
}
class ET2 implements Runnable{
public static int count = 0;
private int no;
private ET1 et1;
/**
* 锁对象
*/
Object o;
public ET2(Object o,ET1 et1) {
this.o = o;
this.et1 = et1;
this.no = count++;
}
@Override
public void run() {
System.out.println(this+"等待获得对象锁...");
synchronized(o) {
System.out.println(this+"获得对象锁...");
o.notify();
System.out.println(this+"释放锁");
}
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
@Override
public String toString() {
return "ET2 [no=" + no + "]";
}
}
输出结果:
ET1获得锁...
ET1被中断...
ET1被中断但是条件不满足,继续休眠...
ET2 [no=3]等待获得对象锁...
ET2 [no=3]获得对象锁...
ET2 [no=3]释放锁
ET2 [no=4]等待获得对象锁...
ET2 [no=4]获得对象锁...
ET2 [no=4]释放锁
ET2 [no=0]等待获得对象锁...
ET2 [no=0]获得对象锁...
ET2 [no=0]释放锁
ET1被中断但是条件不满足,继续休眠...
ET2 [no=1]等待获得对象锁...
ET2 [no=1]获得对象锁...
ET2 [no=1]释放锁
ET2 [no=2]等待获得对象锁...
ET2 [no=2]获得对象锁...
ET2 [no=2]释放锁
ET1被中断但是条件不满足,继续休眠...
可以看出,程序不会结束,因为t1的condition不满足,它要一直休眠,而且每次ET2的线程唤醒t1都会在catch语句继续往下执行。
public class WaitNotifyTest5{
public static void main(String...args) throws InterruptedException {
Object o = new Object();
/*创建6个线程,1个ET1和5个ET2*/
ET1 et1 = new ET1(o);
Thread t1 = new Thread(et1);
Thread t2_1 = new Thread(new ET2(o,et1));
Thread t2_2 = new Thread(new ET2(o,et1));
Thread t2_3 = new Thread(new ET2(o,et1));
Thread t2_4 = new Thread(new ET2(o,et1));
Thread t2_5 = new Thread(new ET2(o,et1));
//先让t1执行
t1.start();
Thread.sleep(5000);
t2_1.start();t2_2.start();t2_3.start();t2_4.start();t2_5.start();
t1.interrupt();
//五个线程中会有一个线程会先获得锁,这个线程执行完会唤醒wait的t1并且将其休眠条件置为true,让t1和剩下的四个线程一起竞争锁。
Thread.sleep(20000);
}
}
class ET1 implements Runnable{
//线程的休眠条件,只要condition为false,线程就要休眠
private boolean condition = false;
/**
* 锁对象
*/
Object o;
public ET1(Object o) {
this.o = o;
}
@Override
public void run() {
synchronized(o) {
System.out.println("ET1获得锁...");
try {
//防止虚假唤醒(虚假唤醒的时候condition不一定满足),所以重复检查条件
while(!condition) {
o.wait();
}
System.out.println("ET1 after wait...");
} catch (InterruptedException e) {
System.out.println("ET1被中断...");
//被中断也要检查条件,不满足也要继续休眠
while(!condition) {
try {
System.out.println("ET1被中断但是条件不满足,继续休眠...");
o.wait();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
System.out.println("ET1执行完毕释放锁...");
}
public boolean isCondition() {
return condition;
}
public void setCondition(boolean condition) {
this.condition = condition;
}
}
class ET2 implements Runnable{
public static int count = 0;
private int no;
private ET1 et1;
/**
* 锁对象
*/
Object o;
public ET2(Object o,ET1 et1) {
this.o = o;
this.et1 = et1;
this.no = count++;
}
@Override
public void run() {
System.out.println(this+"等待获得对象锁...");
synchronized(o) {
System.out.println(this+"获得对象锁...");
o.notify();
//设置et1的条件为true,这样et1就不会进入休眠
et1.setCondition(true);
System.out.println(this+"释放锁");
}
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
@Override
public String toString() {
return "ET2 [no=" + no + "]";
}
}
ET1获得锁...
ET1被中断...
ET1被中断但是条件不满足,继续休眠...
ET2 [no=0]等待获得对象锁...
ET2 [no=2]等待获得对象锁...
ET2 [no=3]等待获得对象锁...
ET2 [no=4]等待获得对象锁...
ET2 [no=1]等待获得对象锁...
ET2 [no=0]获得对象锁...
ET2 [no=0]释放锁
ET1执行完毕释放锁...
ET2 [no=1]获得对象锁...
ET2 [no=1]释放锁
ET2 [no=4]获得对象锁...
ET2 [no=4]释放锁
ET2 [no=3]获得对象锁...
ET2 [no=3]释放锁
ET2 [no=2]获得对象锁...
ET2 [no=2]释放锁
可以看到waitNotifyTest5的和waitNotifyTest4的区别是5的ET2执行完毕后会将ET1对象的condition置为true,这样t1就不会休眠了。所有的任务可以执行完毕。
wait、notify、notifyAll比较经典的用法是生产者消费者的例子。下面是一个例子:
package btp.oneP;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 现在有很多卖永康肉麦饼的店子,一个做饼的师傅,台子上有盘子,假定盘子只能放一定数量的
* 饼,还有很多客户来买。
* @author baitp
*
*/
public class YongkangMeatWheatCakeTest {
public static void main(String[] args) throws InterruptedException {
//饼盘子
BlockingQueue<YongkangMeatWheatCake> bq = new ArrayBlockingQueue<YongkangMeatWheatCake>(10);
//一个师傅
Thread saysay = new Thread(new Craftsman(bq));
//多个客户
Thread c1 = new Thread(new Customer(bq));
Thread c2 = new Thread(new Customer(bq));
Thread c3 = new Thread(new Customer(bq));
Thread c4 = new Thread(new Customer(bq));
Thread c5 = new Thread(new Customer(bq));
//启动
saysay.start();
c1.start();c2.start();c3.start();c4.start();c5.start();
Thread.sleep(20000);
}
}
/**
* 做饼师傅
*
*/
class Craftsman implements Runnable{
/**
* 放饼的盘子,师傅往里面放饼
*/
private BlockingQueue<YongkangMeatWheatCake> bq;
public Craftsman(BlockingQueue<YongkangMeatWheatCake> bq) {
this.bq = bq;
}
@Override
public void run() {
//师傅要一直做饼,所以放在循环中
while(true) {
//因为饼盘是唯一的,使用它作为对象锁
synchronized(bq) {
//饼盘满的时候,师傅休息。
while(bq.remainingCapacity() == 0) {
try {
System.out.println("盘子满了,师傅休息...");
bq.wait();
} catch (InterruptedException e) {
System.out.println("师傅被打断了...");
}
}
//师傅往盘子里放饼
YongkangMeatWheatCake makedCake = new YongkangMeatWheatCake();
bq.add(makedCake);
System.out.println("师傅做了个"+makedCake+"放入盘子中");
//唤醒因为饼盘空而等到的顾客,和刚来的顾客一起竞争买饼(假装不排队吧(`へ´*)ノ)
bq.notifyAll();
}
}
}
}
/**
* 消费者
*
*/
class Customer implements Runnable{
public static int count = 0;
private int no;
/**
* 放饼的盘子,消费者从里面拿饼
*/
private BlockingQueue<YongkangMeatWheatCake> bq;
/**
* 买到的饼
*/
private YongkangMeatWheatCake myCake;
public Customer(BlockingQueue<YongkangMeatWheatCake> bq) {
this.bq = bq;
no = ++count;
}
@Override
public void run() {
System.out.println(this+"来买饼了...");
//因为饼盘是唯一的,使用它作为对象锁
synchronized(bq) {
System.out.println(this+"准备买饼...");
//盘子空的时候,客户等待
while(bq.isEmpty()) {
try {
System.out.println("饼盘空了,"+this+"等待...");
//盘子空的时候,叫醒其他等待的线程,这其中包括师傅,师傅就可以做饼
bq.notifyAll();
bq.wait();
} catch (InterruptedException e) {
System.out.println("顾客被中断了...");
}
}
myCake = bq.poll();
System.out.println("哇!"+this+"买到了"+myCake);
//通知师傅
bq.notifyAll();
}
}
@Override
public String toString() {
return "Customer"+no;
}
}
/**
* 永康肉麦饼类
*/
class YongkangMeatWheatCake{
public static int count = 0;
private int cakeNo;
public YongkangMeatWheatCake() {
cakeNo = ++count;
}
@Override
public String toString() {
return "饼 " + cakeNo;
}
}
Thread#sleep(millis)
/**
* 使当前的线程停止millis毫秒的时间。
* 线程调用sleep(millis)并不会失去锁的持有权。
*
* @param millis
* 线程停止执行的毫秒数
*
* @throws IllegalArgumentException
* millis为负数
*
* @throws InterruptedException
* 如果线程被其他线程中断,则线程的中断状态
* 会被清除并抛出此异常。
*/
public static native void sleep(long millis) throws InterruptedException;
Thread#sleep(millis, nanos)
sleep(millis, nanos)比sleep(millis)多了一个纳秒数,其余并无分别。
sleep和wait很像,都是停止执行当前的线程,但是两者最大的不同是sleep不会放弃自己持有的锁对象,而wait会将锁对象的持有权交出。
public class SleepTest {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
Thread t1 = new Thread(new ST1(o));
Thread t2 = new Thread(new ST2(o));
t1.start();
Thread.sleep(4000);
t2.start();
Thread.sleep(20000);
}
}
class ST1 implements Runnable{
private Object o;
public ST1(Object o) {
this.o = o;
}
@Override
public void run() {
System.out.println("ST1 get CPU...");
synchronized(o) {
System.out.println("ST1获得锁...");
try {
Thread.sleep(10000L);
System.out.println("ST1 after sleep and get CPU");
} catch (InterruptedException e) {
System.out.println("ST1被中断...");
}
System.out.println("ST1准备释放锁...");
}
System.out.println("ST1 realease CPU...");
}
}
class ST2 implements Runnable{
private Object o;
public ST2(Object o) {
this.o = o;
}
@Override
public void run() {
System.out.println("ST2 get CPU...");
synchronized(o) {
System.out.println("ST2获得锁...");
}
System.out.println("ST2 realease CPU...");
}
}
输出:
ST1 get CPU...
ST1获得锁...
ST2 get CPU...
ST1 after sleep and get CPU
ST1准备释放锁...
ST2获得锁...
ST1 realease CPU...
ST2 realease CPU...
从输出结果可以看出,ST2 get CPU之后并没有向下继续执行,因为ST1 sleep之后依然持有锁。sleep方法也会抛出InterruptedException,所以也是可中断的方法。
Thread#join
/**
* 等待调用join()方法的线程死亡,当前线程才继续往下运
* 行。
* 比如在线程t1代码中调用了t2.join(),则t1要等t2死亡
* 之后才继续执行。
*
* join()和join(0)效果一样。
*
* @throws InterruptedException
* 如果线程代码中调用了join,线程被其它线程
* 中断,会抛出此异常,且中断状态会被清除。
* 其实就是如果t1中代码有t2.join(),那意思
* 就是t1等待t2先完成再继续,但是中断在
* 这里的意思是t1不想等t2了,就把t1中断了,
* t1就不必等t2完成再完成。
*/
public final void join() throws InterruptedException {
join(0);
}
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
Thread jt2 = new Thread(new JT2());
Thread jt1 = new Thread(new JT1(jt2));
jt2.start();
Thread.sleep(3000);
jt1.start();
//jt1.interrupt();此时jt1没必要等jt2完成再继续
Thread.sleep(20000);
}
}
class JT1 implements Runnable {
private Thread t;
public JT1(Thread t) {
this.t= t;
}
@Override
public void run() {
System.out.println("JT1 executing...");
try {
t.join();
System.out.println("JT1 after JT2 join...");
} catch (InterruptedException e) {
//Thread.currentThread().interrupt();
System.out.println("JT1 interrupted...");
}
}
}
class JT2 implements Runnable{
@Override
public void run() {
/*while(true) {//这样的话ST1就永远不会继续往下执行了}*/
System.out.println("JT2 executing...");
try {
Thread.sleep(10000);
System.out.println("JT2 after sleep...");
} catch (InterruptedException e) {
System.out.println("JT2 interrupted...");
//Thread.currentThread().interrupt();
}
}
}
输出结果:
JT2 executing...
JT1 executing...
JT2 after sleep...
JT1 after JT2 join...
可以看出,JT1 after JT2 join…在JT2 after sleep…之后。
Thread#join(millis)
/**
* 等待调用join(millis)方法的线程死亡millis毫秒,如果millis毫秒时间后调用线程还未死亡或者
* millis毫秒时间内调用线程就死亡了,当前线程继续执行。
* join(0)和join()效果一样,会一直等下去,直到调用线程死亡。
*
* join(millis)使用循环检查this.isAlive(),判断调用线程是否死亡。而且join是使用wait
* 来实现的,所以其他线程调用调用线程的notifyAll()方法就会破坏join方法,所以推荐应用程序
* 中不要使用线程作为对象锁。
*
* @param millis
* 等待的最大时长
*
* @throws IllegalArgumentException
* millis是负数
*
* @throws InterruptedException
* 同join()
*/
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join是使用wait来实现的,比如在线程t1代码中调用t2.join(),则实际上就和下面代码效果一样:
synchronized(t2) {
...
t2.wait(0);
...
}
t1获得了t2的对象锁,wait方法使t1休眠等待被唤醒。而我们调用join的时候并没有显示去调用t2的notify方法,只是t2运行结束或者时间到了t1就自动向下运行了,所以可能是时间到了或者t2死亡了本地方法会执行和notify类似的底层代码去唤醒线程,让其获得t2的锁并继续往下执行。
join(millis)和join()类似,只是多了一个毫秒数参数,所以比join()多一个停止等待的条件:时间到了。
Thread#join(long millis, int nanos)
join(long millis, int nanos)比join(long millis)多一个纳秒,其余大致相同。join(0, 0)、join(0)和join()效果一致。
join方法也是可被中断的方法。
Thread#yield()
/**
* 暗示调度程序调用此方法的线程愿意让出当前的处理器使
* 用权,处理程序可以忽略此暗示(不理会)。
*
* yield()是一种建议性的尝试,是为了改善线程间的轮转
* 执行,以免过度使用CPU。使用此方法应该结合详细的分
* 析和测试以保证能有你想要的效果。
*
* 很少会用到此方法,它可能在调试和测试方面比较有用,
* 它可以去重现一些竞态条件引起的bug,
* 它也在设计 java.util.concurrent.locks之类的包
* 时被用到。
*/
public static native void yield();
yield方法将线程从运行状态变为可运行状态,也就是说yield之后的线程还可能被CPU再次调度,也就是让程序员感觉没什么效果。
public class YieldTest {
public static void main(String[] args) throws InterruptedException {
Thread yt1 = new Thread(new YT1());
Thread yt2 = new Thread(new YT2());
yt1.start();
yt2.start();
Thread.sleep(20000);
}
}
class YT1 implements Runnable{
@Override
public void run() {
System.out.println("YT1 get CPU...");
Thread.yield();
System.out.println("YT1 after yield...");
}
}
class YT2 implements Runnable{
@Override
public void run() {
System.out.println("YT2 get CPU...");
Thread.yield();
System.out.println("YT2 after yield...");
}
}
输出结果:
YT2 get CPU...
YT1 get CPU...
YT1 after yield...
YT2 after yield...