线程 方法
method | 什么类的方法 | 描述 |
---|---|---|
wait() notify() notifyAll() | Object的本地final方法,无法被重写 | 实现线程间的通信 在线程中调用 wait() 方法,将阻塞当前线程,直至等到其他线程调用了调用 notify() 方法或 notifyAll() 方法进行通知之后,当前线程才能从 wait() 方法出返回,继续执行下面的操作。 |
park() unpark() | 工具类LockSupport | 1.park和unpark是以线程为单位精确阻塞和唤醒线程的; 2.阻塞和唤醒的先后执行顺序可以不一样; |
await和signal | Condition | Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是java底层级别的,后者是语言级别的,具备更高的可控制性和扩展性 |
join() | thread | 控制线程执行顺序 |
wait、notify、notifyAll
wait
该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。
在调用 wait()之前,线程必须要获得该对象的对象监视器锁,即只能在同步方法或同步块中调用 wait()方法。
调用 wait()方法之后,当前线程会释放锁。
如果调用 wait()方法时,线程并未获取到锁的话,则会抛出 IllegalMonitorStateException异常,这是一个 RuntimeException。
如果再次获取到锁的话,当前线程才能从 wait()方法处成功返回。
notify
该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,如果调用 notify()时没有持有适当的锁,也会抛出 IllegalMonitorStateException。
该方法任意从 WAITTING 状态的线程中挑选一个进行通知,使得调用 wait()方法的线程从等待队列移入到同步队列中,等待有机会再一次获取到锁,从而使得调用 wait()方法的线程能够从 wait()方法处退出。调用 notify 后,当前线程不会马上释放该对象锁,要等到程序退出同步块后,当前线程才会释放锁。
notifyAll
该方法与 notify ()方法的工作方式相同,重要的一点差异是:
notifyAll 使所有原来在该对象上 wait 的线程统统退出 WAITTING 状态,使得他们全部从等待队列中移入到同步队列中去,等待下一次能够有机会获取到对象监视器锁。
wait()与notify()操作会释放锁吗?
先说结论:
wait()会立即释放对象的锁
notify() 不会立即释放锁 当执行完同步代码块就会释放对象的锁
wait/notify 消息通知潜在的一些问题
- notify 早期通知
notify 通知的遗漏很容易理解,即 threadA 还没开始 wait 的时候,threadB 已经 notify 了,这样,threadB 通知是没有任何响应的,当 threadB 退出 synchronized 代码块后,threadA 再开始 wait,便会一直阻塞等待,直到被别的线程打断。
- 解决方法:
一般是添加一个状态标志,让 waitThread 调用 wait 方法前先判断状态是否已经改变了没,如果通知早已发出的话,WaitThread 就不再去 wait。
park/unpark
主要方法
Park/UnPark方法是LockSupport当中的方法。
其常用方法有如下:
- park():暂停当前线程。
- park(Object blocker):暂停当前线程,并指定负责此线程停放的同步对像。
- parkNanos(long nanos):暂停当前线程,指定等待的最大纳秒数。
- parkNanos(Object blocker, long nanos):暂停当前线程,指定等待的最大纳秒数和负责此线程停放的同步对像。
- parkUntil(long deadline):暂停当前线程,指定暂停到的时间点。
- parkUntil(long deadline):暂停当前线程,指定暂停到的时间点和负责此线程停放的同步对像。
- unpark(Thread thread):恢复某个线程的运行。
线程可再次运行的条件,这里根据上面的park()方法对号入座:
- 有线程调用unpark方法
- 指定等待时间过时
- 指定的截止日期过时
- 其他线程中断当前线程
优势
与 Object 的 wait/notify 相比:
wait,notify 和 notifyAll 必须配合 Monitor(重量级锁) 一起使用,而 park,unpark 不必。
park/unpark 是以线程为单位来阻塞和唤醒线程,而 notify 只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,无法做到精确唤醒。
park/unpark 可以先 unpark,而 wait/notify 不能先 notify。
park和unpark原理
park和unpark会调用Unsafe类中的native方法
每个线程都会和一个park对象关联起来,由三部分组成 _counter , _cond 和 _mutex。核心部分是counter,我们可以理解为一个标记位。
当调用park时会看counter是否为0,为0则进入阻塞队列。为1则继续运行并将counter置为0。
当调用unpark时,会将counter置为1,若之前的counter值为0,还唤醒阻塞的线程。
await signal
Condition简介
任何一个java对象都自然继承于Object类,在线程间实现通讯的每每会应用到Object的几个方法,好比wait(),wait(long timeout),wait(long timeout, int nanos)与notify(),notifyAll()几个方法实现等待/通知机制,一样的, 在java Lock体系下依然会有一样的方法实现等待/通知机制。从总体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是java底层级别的,后者是语言级别的,具备更高的可控制性和扩展性。二者除了在使用方式上不一样外,在功能特性上仍是有不少的不一样:java
- Condition可以支持不响应中断,而经过使用Object方式不支持;
- Condition可以支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;
- Condition可以支持超时时间的设置,而Object不支持
参照Object的wait和notify/notifyAll方法,Condition也提供了一样的方法:node
针对Object的wait方法
void await() throws InterruptedException:当前线程进入等待状态,若是其余线程调用condition的signal或者signalAll方法而且当前线程获取Lock从await方法返回,若是在等待状态中被中断会抛出被中断异常;
long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时;
boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位
boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中断或者到了某个时间
针对Object的notify/notifyAll方法
void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,若是在同步队列中可以竞争到Lock则能够从等待方法中返回。
void signalAll():与1的区别在于可以唤醒全部等待在condition上的线程