线程 方法

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上的线程

参考文章

评论