java中sleep、interrupt、wait、notify、join、yield用法

1) interrupt是Thread对象的方法,用于修改线程的打断状态为true(可以用isInterrupted()来获取),除此之外未做任何其他事,所以interrupt并不会去真正的打断某个线程的运行状态或杀死某个线程;

但是,当本线程在sleep阻塞或者wait阻塞或者join阻塞(本质还会wait阻塞)时,如果被interrupt,那么会在sleep或wait或join方法内抛出异常InterruptedException(可理解为本线程正在阻塞,没办法设置interrupt的状态,所以抛出异常),此时通过对异常InterruptedException的处理策略来决定本线程的后续工作:

a)自始至终不捕获异常,则会因异常而结束;

b)捕获后return,则结束本线程;

c)捕获后catch内的代码为空,则忽略此异常继续任务,此时interrupt起到一个打断阻塞的作用;

  所以interrupt只是去修改线程的打断状态,相当于是去提示线程,当线程未占用cpu时会抛出异常,其他的事情都有线程本身来决定,不会让调用interrupt的线程来决定,这是与notify有着本质不同的区别;

2) sleep是Thread的方法,让当前线程进入sleep阻塞(sleep block)一段时间(让出cpu)然后进入runable状态,如果当前线程此刻持有着同步锁,那么sleep阻塞的时候并不会释放同步锁;

注:sleep方法只可以在当前线程的内调用,是Thread类的静态方法,如主线程main与子线程t1,则在主线程内执行main.sleep(1000)是主线程暂停一秒,在子线程内调用t1.sleep(1000)是子线程暂停一秒,在主线程内调用t1.sleep(1000)还是主线程暂停一秒而不是子线程;

3) wait是Object对象的方法,

a) 是native方法,必须用在同步锁内的代码中,必须由持有当前同步锁的对象来调用wait方法,

b) 使当前线程(当前的同步锁代码)进入wait阻塞(wait block),wait阻塞会让出此对象持有的同步锁,

这是与sleep的重要区别

wait(1000)会阻塞一秒后自动进入runnable状态,或者被interrupt或notify进入runnable状态;

wait( )无参方法或wait(0)会一直阻塞下去,除非被interrupt或notify;

但是当持锁对象是线程对象时(即Thread对象),此线程任务执行完毕后会调用notifyAll方法使所有此对象的wait阻塞被激活进入runnable状态,即使不被interrupt或notify也可以继续执行;

4) notify或notifyAll也是Object对象的方法,是与wait配合使用的,必须用在同步锁内的代码中,必须由持有当前同步锁的对象来调用,所以往往是在一个线程内调用a.wait(),然后在另一个先线程内调用a.notify()或a.notifyAll(),a为同一个对象;

先说两个概念:锁池和等待池
锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中;
然后再来说notify和notifyAll的区别 如果线程调用了对象的wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
综上,所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。

Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信

5) join( )  join( 1000)方法的作用是让出当前线程的执行权给另外一个线程(一定时间),可以控制线程执行顺序,如在main线程中执行

System.out.println("1");

t1.join();

System.out.println("2");

t2.join(1000);

System.out.println("3");

则main线程在输出1之后等待,让线程t1来执行,t1指向完毕后输出2,然后再次等待,等待t2执行完毕后或等待一秒后继续运行,输出3;

从join()的源码看,如果线程被生成了,但还未被起动,调用它的 join() 方法是没有作用的,将直接继续向下执行;

join( 0)与join( )一样,是一直等待下去,直至线程执行完毕;

join是一个有synchronized修饰的方法,其内部还是调用了wait方法(小提示:Object 提供的方法), 其实join方法本质就是利用线程实例作为对象锁的原理,在join方法内调用“线程实例.wait()”(是会判断线程示例是否在运行中isAlive),保持在wait阻塞状态,当线程终止时,会调用线程自身的notifyAll()方法,通知所有等待在该线程对象上的线程的特征,这样外面的线程就可继续执行了。

当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁(所以t不一定是在调用join后立即执行,如果锁不空闲则需要等待锁)

6) yield Thread的静态方法,是native方法,该方法用于使当前线程主动让出档次cpu时间片,回到runnable状态,的等待下次分配时间片;因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

7) 

Object.wait(),Object.notify(),Object.notifyAll()都是Object的方法,换句话说,就是每个类里面都有这些方法。

Object.wait():释放当前对象锁,并进入阻塞队列
Object.notify():唤醒当前对象阻塞队列里的任一线程(并不保证唤醒哪一个)
Object.notifyAll():唤醒当前对象阻塞队列里的所有线程
为什么这三个方法要与synchronized一起使用呢?解释这个问题之前,我们先要了解几个知识点
每一个对象都有一个与之对应的监视器
每一个监视器里面都有一个该对象的锁以及一个等待队列(等待池)和一个同步队列(锁池)

wait()方法的语义有两个,一是释放当前对象锁,另一个是进入等待队列,
如果不在synchronized中(或非所对应的对象来wait)则当前线程没有获得此对象的锁,自然不能释放此对象的锁,会报错;
notify()方法也是一样的,用来唤醒一个线程,你要去唤醒,
如果不在synchronized中(或非所对应的对象来notify)则当前线程没有获得此对象的锁,自然不能此对象的锁,会报错;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值