以下是本文目录大纲:
一.wait()、notify()和notifyAll()
二.Condition
三.生产者-消费者模型的实现
若有不正之处请多多谅解,并欢迎批评指正。
请尊重作者劳动成果,转载请标明原文链接:
http://www.cnblogs.com/dolphin0520/p/3920385.html
一.wait()、notify()和notifyAll()
wait()、notify()和notifyAll()是Object类中的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/**
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the wait methods
*/
public
final
native
void
notify();
/**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* wait methods.
*/
public
final
native
void
notifyAll();
/**
* Causes the current thread to wait until either another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object, or a
* specified amount of time has elapsed.
* <p>
* The current thread must own this object's monitor.
*/
public
final
native
void
wait(
long
timeout)
throws
InterruptedException;
|
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程,唤醒哪个线程并不确定,并且由该线程去获得锁;
4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程,但是哪个线程获得锁还不一定
wait和sleep的区别:
1. sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
2. 如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此必须在锁中执行(例如在Synchronized代码块中执行)
sleep和yield的区别:
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
下面看一个例子就明白了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public
class
Test {
public
static
Object object =
new
Object();
public
static
void
main(String[] args) {
Thread1 thread1 =
new
Thread1();
Thread2 thread2 =
new
Thread2();
thread1.start();
try
{
Thread.sleep(
200
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
static
class
Thread1
extends
Thread{
@Override
public
void
run() {
synchronized
(object) {
try
{
object.wait();
}
catch
(InterruptedException e) {
}
System.out.println(
"线程"
+Thread.currentThread().getName()+
"获取到了锁"
);
}
}
}
static
class
Thread2
extends
Thread{
@Override
public
void
run() {
synchronized
(object) {
object.notify();
System.out.println(
"线程"
+Thread.currentThread().getName()+
"调用了object.notify()"
);
}
}
}
}
|
无论运行多少次,运行结果必定是:

线程Thread-1调用了object.notify() 线程Thread-1释放了锁 线程Thread-0获取到了锁
二.Condition
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。
- Condition是个接口,基本的方法就是await()和signal()方法;
- Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
- 调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()。
Condition的特性:
1.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。
2.Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,"读线程"需要等待。
如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。
三.生产者-消费者模型的实现
1.使用Object的wait()和notify()实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
public
class
Test {
private
int
queueSize =
10
;
private
PriorityQueue<Integer> queue =
new
PriorityQueue<Integer>(queueSize);
public
static
void
main(String[] args) {
Test test =
new
Test();
Producer producer = test.
new
Producer();
Consumer consumer = test.
new
Consumer();
producer.start();
consumer.start();
}
class
Consumer
extends
Thread{
@Override
public
void
run() {
consume();
}
private
void
consume() {
while
(
true
){
synchronized
(queue) {
while
(queue.size() ==
0
){
try
{
System.out.println(
"队列空,等待数据"
);
queue.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.poll();
//每次移走队首元素
queue.notify();
System.out.println(
"从队列取走一个元素,队列剩余"
+queue.size()+
"个元素"
);
}
}
}
}
class
Producer
extends
Thread{
@Override
public
void
run() {
produce();
}
private
void
produce() {
while
(
true
){
synchronized
(queue) {
while
(queue.size() == queueSize){
try
{
System.out.println(
"队列满,等待有空余空间"
);
queue.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.offer(
1
);
//每次插入一个元素
queue.notify();
System.out.println(
"向队列取中插入一个元素,队列剩余空间:"
+(queueSize-queue.size()));
}
}
}
}
}
|
2.使用Condition实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
public
class
Test {
private
int
queueSize =
10
;
private
PriorityQueue<Integer> queue =
new
PriorityQueue<Integer>(queueSize);
private
Lock lock =
new
ReentrantLock();
private
Condition notFull = lock.newCondition();
private
Condition notEmpty = lock.newCondition();
public
static
void
main(String[] args) {
Test test =
new
Test();
Producer producer = test.
new
Producer();
Consumer consumer = test.
new
Consumer();
producer.start();
consumer.start();
}
class
Consumer
extends
Thread{
@Override
public
void
run() {
consume();
}
private
void
consume() {
while
(
true
){
lock.lock();
try
{
while
(queue.size() ==
0
){
try
{
System.out.println(
"队列空,等待数据"
);
notEmpty.await();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
//每次移走队首元素
notFull.signal(); // 只唤醒生产者线程
System.out.println(
"从队列取走一个元素,队列剩余"
+queue.size()+
"个元素"
);
}
finally
{
lock.unlock();
}
}
}
}
class
Producer
extends
Thread{
@Override
public
void
run() {
produce();
}
private
void
produce() {
while
(
true
){
lock.lock();
try
{
while
(queue.size() == queueSize){
try
{
System.out.println(
"队列满,等待有空余空间"
);
notFull.await();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(
1
);
//每次插入一个元素
notEmpty.signal();
System.out.println(
"向队列取中插入一个元素,队列剩余空间:"
+(queueSize-queue.size()));
}
finally
{
lock.unlock();
}
}
}
}
}
|