线程sleep与wait的区别(拓展yield和join)

线程sleep与wait的区别(拓展yield和join)

关于这道题(多选题)关于 sleep()和 wait(),以下描述正确的一项是( )

​ A.sleep 是线程类(Thread)的方法,wait 是 Object 类的方法;

​ B.sleep 不释放对象锁,wait 放弃对象锁;

​ C.sleep 暂停线程、但监控状态仍然保持,结束后会自动恢复;

​ D.wait 后进入等待锁定池,只有针对此对象发出 notify 方法后获得 对象锁进入运行状态

解析:我们先整理下sleep和wait的区别

sleep

​ sleep是Thread提供的静态方法,作用是暂停此线程,这时候呢其他线程就可以进行,也就是在整个线程队列中重新排队,直到睡眠结束后等待CPU的调度(注意,这时候并不代表它具有高优先级,一样要同其他线程竞争,所以并不一定会马上执行)。还有一个最大的区别是它并没有放弃对象锁,仍然保持监控状态,结束后呢就会自动恢复继续往下执行。

wait

​ wait是Object提供的方法,执行时,当前线程将被放入等待池(注意不是锁池),放开对象锁,其他的线程就可以争夺这个锁,这时需要等待notify或者notifyAll或者指定的睡眠时间唤醒线程池中的线程,被唤醒后就去争夺锁(仍然在等待池,只不过是有了争夺锁的权限),抢到后继续往下执行。

回到我们的题目中,ABC不用多说,关于D,这里要注意题说的“只有”,我们知道是notify和notifAll或者指定的睡眠时间到了就可以获得,所以这里D是错的。

这里拓展下yield和join方法的作用与区别

yield

​ 作用跟sleep类似,放弃当前CPU权限暂停当前线程,不会放弃锁对象。区别在于暂停当前进程,是为了让更高优先级或者同优先级的进程先执行,那什么时候会恢复由线程调度器说了算,比如执行yield时没有其他更高优先级的线程要执行时,那么该线程就会恢复。

join

​ join的作用是让加入的线程先执行,被插入的线程等待它执行完才能执行,可以理解为插队。例如如下代码,按照理解是t1会先执行完后t2才能执行

public static void main(String[] args) {
    ...//省略创建线程对象步骤
    t1.start();
    t1.join(); //调用wait方法阻塞当前main方法,等待t1执行完
    t2.start();
}

join释放对象锁问题

有一个问题,如果t1、t2两个进程抢的是一个锁对象,那么被(插队)阻塞的线程会不会放开锁呢。我们来看看join方法的源码

if (millis == 0) {
    while (isAlive()) {
        wait(0);
    }

可以看到实际上join方法调用的是wait方法,那么我们知道wait方法是会放弃对象锁的,而join方法是当前线程放弃是调用者t1这个对象锁(谁调用就是谁xx.join就是放弃的xx对象锁),而不是线程中的抢的锁对象。看下面例子:

package com.wangshili;
public class TestJoinMethod {
	static Integer number = 1000;
	public static void main(String[] args) {
		Thread t1 = new Thread() { //创建t1线程
			@Override
			public void run() {
				while(true) {
					synchronized (number) {  //这里抢的是number锁
						if(number <= 0)break;
						number = number -1;
						System.out.println(getName()+":"+number);
					}
				}
			}
		};
		Thread t2 = new Thread() {
			@Override
			public void run() {
				while(true) {
					synchronized (number) {
						try {
							System.out.println(getName()+"抢到锁"); //t2抢到锁后t1插队
							t1.join();//t1插队,t2线程将会放弃t1线程锁,不是number
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						if(number <= 0)break;
						number = number -1;
						System.out.println(getName()+":"+number);
					}
				}
			}
		};
		
		t1.start();//启动线程
		t2.start();
	}

}

我们看下执行结果,会发现每当t2抢到锁,t1插入,这个线程就会卡死,这就是因为它们抢的锁是number这个对象,而join方法让当前线程放弃的对象锁是t1,所以就会在t1线程的while中卡死,不断的抢锁,而t2是拿着锁的。

在这里插入图片描述

我们修改下代码就可以知道,让两个线程抢t1的锁

package com.wangshili;
public class TestJoinMethod {
	static Integer number = 1000;
	static Thread t1 =null;//提前定义t1变量
	public static void main(String[] args) {

		 t1 = new Thread() {
			@Override
			public void run() {
				while(true) {
					synchronized (t1) {  //换成抢t1的锁
						if(number <= 0)break;
						number = number -1;
						System.out.println(getName()+":"+number);
					}
				}
			}
		};
		Thread t2 = new Thread() {
			@Override
			public void run() {
				while(true) {
					synchronized (t1) {//换成抢t1的锁
						try {
							System.out.println(getName()+"抢到锁");
							t1.join();//t1插队
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						if(number <= 0)break;
						number = number -1;
						System.out.println(getName()+":"+number);
					}
				}
			}
		};
		
		t1.start();//启动线程
		t2.start();
	}

}

在这里插入图片描述

从运行结果来看,很明显,当抢的锁和join方法对象一致的时候,就不会出现卡死情况,说明t2已经放弃了锁,t1拿到锁执行语句。

总结

​ 几种方法用的场合不一样,了解最大的区别就可以了。像wait和sleep一个不放弃对象锁,一个是放弃对象锁,wait是放弃的对象锁是引用它的对象,不要与线程中的加锁对象搞混。yield与sleep类似但不可以指定休眠时间,所以用的相对较少,join最大的区别就是插队了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值