一、线程分类
二、线程的实现方式
三、线程的状态
四、线程的核心方法
sleep()方法:该方法属于Thread类,是线程用来控制自身流程的,在线程休眠的过程中不会释放所持有的对象锁。
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权;wait()方法:该方法属于Object类且为final方法,无法被子类覆写,用来线程间的通信,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。此方法一般与notify()或notifyAll()方法配套使用。
notify()方法:该方法属于Object类且为final方法,无法被子类覆写,用来线程间通信。通常由当前对象调用该方法形如Object.notify()唤醒一个正在等待该对象的monitor(锁)的线程,当有多个线程都在等待该对象的monitor(锁)的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
notifyAll()方法:该方法属于Object类且为final方法,无法被子类覆写,用来线程间通信。notifyAll()方法能够唤醒所有正在等待该对象的monitor(锁)的线程,这一点与notify()方法是不同的。notify()和notifyAll()方法只是唤醒等待该对象的monitor(锁)的线程,并不决定哪个线程能够获取到monitor(锁)。你可以用 notify 和 notifyAll 来通知那些在此监视器上等待的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信 号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。wait()会使持有该对象锁的线程释放占有的锁,notify和notifyAll不会使持有该对象锁的线程释放占用的锁。
join()方法:join是Thread类的一个方法,启动线程后直接调用,例如:
Thread t = new AThread(); t.start(); t.join();
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
- interrupt()方法:
interrupt()并不直接中断线程,而是设定一个中断标识,然后由程序进行中断检查,确定是否中断。
- sleep() &interrupt()
线程A正在使用sleep()暂停着:Thread.sleep(100000);
如果要取消他的等待状态,可以在正在执行的线程里(比如这里是B)调用a.interrupt();
令线程A放弃睡眠操作,这里a是线程A对应到的Thread实例执行interrupt()时,并不需要获取Thread实例的锁定.任何线程在任何时刻,都可以调用其他线程interrupt().当sleep中的线程被调用interrupt()时,就会放弃暂停的状态.并抛出InterruptedException.丢出异常的,是A线程. - wait() & interrupt()
线程A调用了wait()进入了等待状态,也可以用interrupt()取消.
不过这时候要小心锁定的问题.线程在进入等待区,会把锁定解除,当对等待中的线程调用interrupt()时(注意是等待的线程调用其自己的interrupt()),会先重新获取锁定,再抛出异常.在获取锁定之前,是无法抛出异常的. - join() & interrupt()
当线程以join()等待其他线程结束时,一样可以使用interrupt()取消之.因为调用join()不需要获取锁定,
故与sleep()时一样,会马上跳到catch块里.注意是随调用interrupt()方法,一定是阻塞的线程来调用其自己的interrupt方法.如在线程a中调用来线程t.join().则a会等t执行完后在执行t.join后的代码,当在线程b中调用来a.interrupt()方法,则会抛出InterruptedException。 - interrupt()只是改变中断状态而已
interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait,Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。
线程A在执行sleep,wait,join时,线程B调用A的interrupt方法,的确这一个时候A会有InterruptedException异常抛出来.但这其实是在sleep,wait,join这些方法内部会不断检查中断状态的值,而自己抛出的InterruptedException。
如果线程A正在执行一些指定的操作时如赋值,for,while,if,调用方法等,都不会去检查中断状态,所以线程A不会抛出InterruptedException,而会一直执行着自己的操作.当线程A终于执行到wait(),sleep(),join()时,才马上会抛出InterruptedException.
若没有调用sleep(),wait(),join()这些方法,或是没有在线程里自己检查中断状态自己抛出InterruptedException的话,那InterruptedException是不会被抛出来的.
五、wait()方法和sleep()方法的区别
相同点:
- 通过Thread.sleep()和Object.wait()方法都可以暂停当前的线程,对于CPU资源来说,不管是哪种方式暂停的线程,都表示它暂时不再需要CPU的执行时间。OS会将执行时间分配给其它线程。
不同点:
- sleep()是Thread类中定义的方法,wait()是Object类中定义的方法;
- 调用Thread.sleep()方法的线程不会释放该线程所拥有的锁,当前对象调用Object.wait()方法后持有该对象锁的线程会释放所持有的对象锁。
- 当前对象调用Object.wait()方法后需要别的线程执行notify()/notifyAll()才能够有机会重新获得CPU执行时间。
六、wait(),notify(),notifyAll()
- 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。
- 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
- 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
- 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
- 相较于notify()方法,多使用notifyAll()方法。
六、代码示例
假设线程A实现了Runnable接口,其run方法如下:
@Override
public void run(){
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
}
}
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait() , 放弃对象锁.交由系统进行线程的调度。
假设线程B也实现了Runnable接口,其run方法如下:
@Override
public void run(){
synchronized(obj) {
condition = true;
obj.notify();
}
}
如果B持有了obj对象锁之后,使得线程A的condition条件满足了,就可以唤醒等待obj对象锁的线程A:
几点说明:
- 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {…} 代码段内。
- 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {…} 代码段内唤醒等待obj对象锁的线程A。
- 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
- 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
- obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
- 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。