一、线程间通信的两种方式
1、wait()/notify()
Object类中相关的方法有notify()、notifyAll()和wait()。因为这些方法是定义在Object类中,因此会被所有的类所继承。这些方法都是final修饰,因而不能被重写。
- wait()方法:让当前进程进入等待状态,并释放锁,同时释放CPU。
- wait(long)方法:让当前线程进入等待,并释放锁,不过等待时间为long,超过这个时间没有对当前线程进行唤醒,将自动唤醒。
- notify()方法:让当前线程通知处于等待状态的线程,当前线程完毕后释放锁,并从其他线程中唤醒其中一个继续执行。
- notifyAll()方法:让当前线程通知处于等待状态的线程,当前线程执行完毕后释放锁,将唤醒所有等待状态的线程。
wait()方法使用注意事项:
- 当前线程拥有当前对象的锁时,才能调用wait()方法,否则将抛出异常java.lang.IllegalMonitorStateException
- 线程调用wait()方法,释放它对锁的使用权,并且等待其他线程通知它(notify()或notifyAll()),这样它才能重新获得锁的使用权和恢复执行。
- 要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
wait()、sleep()、yield()、join()比较
- 线程调用wait()方法时,它会释放对象的锁,并释放CPU,线程放入等待队列中;
- sleep()方法不释放锁,但释放CPU,线程被放入超时等待队列中;
- yield()方法仅释放CPU,锁占用,线程会别放入就绪对列中;
- join()方法释放CPU,也释放锁(join()源码中使用wait()方法),线程被放入超时等待队列中。(join()方法只能管到它所在位置后面的线程。即,join()方法所在位置后面的线程才会一直等待调用该方法的线程执行完,在它前面的不会)。
线程状态:
有资源有CPU执行权:运行态
有资源无CPU执行权:就绪态
无资源无执行器:阻塞态(会一直请求资源)
主动释放资源和执行权,不设置超时时间,需要被唤醒:等待态
主动释放资源和执行权,设置超时时间,能自动唤醒:超时等待态
2、Condition实现等待/通知
关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式,类似Reentrantlock也可以实现同样的功能,但需要借助于Condition对象。
关于Condition实现等待/通知可以类比wait()/notify(),如下:
condition.await() ——————> lock.wait()
condition.await(long time, TimeUnit unit) ——————> lock.wait(long timeout)
condition.signal() ——————> lock.notify()
condition.signalAll() ——————> lock.notifyAll()特殊之处:
synchronized相当于整个ReentrantLock对象只有一个单一的Condition对象情况。
而一个ReentrantLock却可以拥有多个Condition对象,来实现通知部分线程。
具体实现方式:
假设有两个Condition对象:ConditionA和ConditionB。那么由ConditionA.await()方法进入等待状态的线程,由ConditionA.signalAll()通知唤醒;由ConditionB.await()方法进入等待状态的线程,由ConditionB.signalAll()通知唤醒。