看个例子:
public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.setName("myThread"); myThread.start(); try { System.out.println("线程" + Thread.currentThread().getName() + "等待"); myThread.join(); System.out.println("线程" + Thread.currentThread().getName() + "继续执行"); } catch (InterruptedException e) { e.printStackTrace(); } } static class MyThread extends Thread { @Override public void run() { System.out.println("进入线程" + Thread.currentThread().getName()); try { Thread.currentThread().sleep(5000); //模拟执行任务需要5秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("线程" + Thread.currentThread().getName() + "执行完毕"); } } 输出: 线程main等待 进入线程myThread 线程myThread执行完毕 线程main继续执行
由此可以看出,join线程阻断了main线程,执行完成之后,main线程才继续。(实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知)由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。
interrupt方法:顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。
通过interrupt方法可以中断处于阻塞状态的线程,interrupt方法不能中断正在运行中的线程。所以结合isInterrupted方法,可以联合控制程序
stop方法:stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。
destroy方法:destroy方法也是废弃的方法。基本不会被使用到。
getId、getName、setName、currentThread()
getPriority和setPriority:用来获取和设置线程优先级。
setDaemon和isDaemon:用来设置线程是否成为守护线程和判断线程是否是守护线程。
守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。
说了这么方法之后,给出下面这幅图,就更加清晰了:
简要说说sleep和wait的区别:
线程的这么多方法中,此处我重点介绍一下join方法,因为有些场景,还真要他出马:
join底层是wait方法,所以它是会释放对象锁的,而sleep在同步的方法中是不释放对象锁的,只有同步方法执行完毕,其他线程才可以执行。
join方法的使用场景:
曾经做过一个联通的增值业务项目,其中有一个业务需要给联通方暴漏接口,他们调用我们的接口,我们进行业务处理后,再返回结果,接口要求是同步的,实时返回。如果异步的就可以用消息队列解决了,吧整个业务逻辑中比较费时间的都放在了子线程中运行,子线程跑完后在交由主线程返回结果。当时用的是java中的栅栏 CyclicBarrier ,现在想想用join也是可以实现的。