线程基本知识

155 阅读3分钟

线程状态

  1. New:尚未启动的线程的线程状态。
  2. Runnable:可运行线程的线程状态,等待CPU调度。
  3. Blocked:线程锁定等待监视器锁定的状态。处于synchronized同步代码块或方法中被阻塞。
  4. Waiting:等待线程的线程状态。下列不带超时的方式:Object.wait,Thred.join,LockSupport.park
  5. Timed Waiting:具有指定等待时间的等待线程的线程状态。下列带超时的等待状态:Thread.sleep,Object.wait,Thread.join,LockSupport.parkNanos,LockSupport.parkUtil。
  6. Terminated:终止线程的线程状态。线程正常完成或者出现异常。

线程状态切换

  • 一个线程创建出来状态为New。当调用开始的时候,状态变为Runnable。如果与其他线程争抢一把锁且没有获取到锁,就会进入Blocked状态;如果拿到锁就会回到Runnable状态。
  • 线程接受到wait方法或者join方法,线程状态就从Runnable切换到Waiting状态,等待其他线程通知;如果收到其他线程通知,会回到Runnable状态,否则一直是Waiting状态。
  • 线程接受到有超时时间的等待方法,线程状态从Runnable状态切换到Timed Waiting状态;当等待超时或者收到其他线程通知后,会回到Runnable状态。
  • 线程执行成功或者执行异常就会进入Terminated状态。

线程终止

不正确的线程中止

  • Stop:中止线程,并清除监控器锁信息,但是可能导致线程安全的问题,JDK不建议使用。

正确的线程中止

  • interrupt:如果目标线程在调用Object class的wait()、wait(long)或wait(long,int)方法、join()、join(long,int)或sleep(long,int)方法时被阻塞,那么interrupt会生效,线程的中断状态将被清除,抛出InterruptedException异常。如果线程是被IO或者NIO的Channel所阻塞,同样,IO操作会被中断或者返回特殊的异常值。达到终止线程的目的。如果以上条件都不满足,则会设置此线程的一个中断状态。
  • 标志位:代码逻辑中,增加一个判断,用来控制线程执行的中止。

线程通信

要想实现多个线程之间的协同,如:线程执行先后顺序、获取某个线程执行结果等等。涉及到线程的相互通信,分为以下四类:

  • 文件共享
  • 网络共享
  • 共享变量
  • jdk提供的线程协调API:suspend/resume、wait/notify、park/unpark。

线程协作JDK API

JDK对于需要多线程协作完成某一任务的场景,提供了对应的API支持。多线程协作的典型场景是:生产者消费者模型(线程阻塞、线程唤醒)。

suspend/resume(已弃用)

作用:调用suspend挂起目标线程,通过resume可恢复线程执行。
被弃用的主要原因是容易写出死锁代码。首先,suspend被挂起后不会释放锁;其次,必须先执行suspend再执行resume,否则死锁。所以用wait/notify、park/unpark机制对它进行替代。

wait/notify机制

这些方法只能由同一对象锁的持有者线程调用,也就是写在同步代码块里面,否则会抛出IllegalMonitorStateException异常。wait方法导致当前线程等待,加入到对象的等待集合中,并且放弃当前持有的对象锁。notify/notifyAll方法唤醒一个或所有正在等待当前对象锁的线程。
需要注意的是,虽然wait会自动解锁,但是对顺序有要求,如果在notify后才开始wait的调用,线程会永远处于WAITING状态。

park/unpark机制

线程调用park则等待『许可』,unpark为指定线程提供『许可』。不要求调用的先后顺序。多次调用unpark后,再调用park,线程会直接运行,但不会叠加。也就是说,连续多次调用park方法,第一次会拿到许可直接运行,后续调用会进入等待。需要注意的是park方法不会释放锁。