此篇接上一篇Java面试之实现多线程(二)
Java线程可以拥有自己的操作数栈、程序计数器、局部变量表等资源,它与同一进程内的其他线程共享该进程的所有资源。Java线程在生命周期内存在多种状态,可以通过Thread.State枚举类获取线程状态。如图所示有NEW(新建状态)、RUNNABLE(就绪状态)、BLOCKED(阻塞状态)、WAITING(等待状态)、TIMED_WAITING(超时等待状态)、TERMINATED(终止状态)六种状态。
NEW,新建状态,表示线程已经被创建但尚未启动。
创建线程的方式有三种:
第一种继承自Thread类。
优点:实现简单,只需实例化继承类的实例,即可使用线程。
缺点:扩展性不足,因Java单继承的局限性,如果已经继承了一个类,就无法通过这种方式实现自定义线程。
第二种实现Runable接口。
优点:扩展性好,可以在此基础上继承其他类,非常适合多线程处理一份资源的场景。
缺点:构造线程实例的过程相对繁琐。
第三种实现Callable接口。
优点:扩展性好,能支持回调并得到返回值,而且可以抛出受检异常。
缺点:相较于实现Runable接口的方式,调用过程较为繁琐。
推荐使用第二种,因为继承自Thread类往往不符合里氏换代原则。而实现Runable接口可以使编程更加灵活,对外暴露的细节比较少,让使用者专注于实现线程的run()方法上。
Callable与Runnable有两点不同:第一可以通过call()获得返回值。第二call()可以抛出异常。而Runnable只有通过setDefaultUncaughtExceptionHandler()的方式才能在主线程中捕捉到子线程异常。
RUNNABLE,就绪状态。是调用start()之后运行之前的状态。线程的start()不能多次调用,否则会抛出IllegalStateException异常。
RUNING,运行状态。是run()方法正在执行时线程的状态。线程可能会由于某些因素而退出RUNING,如时间,异常,锁,调度等。
BLOCKED,阻塞状态。进入此状态,有以下几种情况。
同步阻塞:锁被其他线程占用。
主动阻塞:调用Threa的某些方法(sleep、join等),主动让出CPU执行权。
等待阻塞:执行wait()。
注:阻塞状态的线程,即使调用interrupt()方法也不会改变其状态。
WAITING,无条件等待状态。如果没有被唤醒或等待的线程没有结束,那么将一直等待。比如调用wait()、join()、park()等不加超时时间的方法之后所处的状态,当前状态的线程不会被分配CPU资源和持有锁。
TIMED_WAITING,有条件的等待。在指定的时间没有被唤醒或者等待线程没有结束,会被系统自动唤醒,正常退出。比如sleep()、wait()、join()、parkNanos()、parkUntil()等带有超时时间的方法之后所处的状态。
TERMINATED,终止状态。run()执行结束。或因异常退出后的状态,此状态不可逆转。
喜欢的朋友记得点赞、收藏、关注哦!!!