wait和notify总结

wait和notify总结

十月 29, 2020

直接上结论

首先这两个东西必须在synchronized里面使用,要用到别的锁会有替代下面两个的东西。

  • wait:挂起当前线程,释放获取到的锁,直到别的线程调用了这个对象的notify或notifyAll方法。wait被唤醒后会根据LIFO原则获得锁(不同虚拟机实现不同,Hotspot是LIFO)。
  • notify:唤醒因调用wait挂起的线程,如果有多个线程,随机唤醒一个,注意notify不会立即释放锁,在退出锁代码块之后才释放。

    其他内置锁

    需要利用condition实现wait和notify。
    1
    2
    3
    Condition condition = lock.newCondition();
    condition.await();
    condition.siginal();

    多线程例题

    一、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
    public class TestMain {
    static List list = new ArrayList();

    public static void main(String[] args) {
    final Object lock = new Object();
    Thread t1 = new Thread(new Runnable() {
    public void run() {
    synchronized (lock) {
    for (int i = 0; i < 10; i++) {
    list.add(i);
    System.out.println("add "+i);
    if (list.size() == 5) {
    try {
    lock.notify();
    lock.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }
    });
    Thread t2 = new Thread(new Runnable() {
    public void run() {
    synchronized (lock) {
    //if (list.size() != 5) {//可有可无
    try {
    lock.wait();
    System.out.println("output!!!!!");
    lock.notify();//在哪notify都一样了,也可以放在锁外边
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    //}
    }

    }
    });
    t2.start();
    t1.start();
    }
    }
    思路:用wait和notify解决,t2先wait,当t1添加五个数时候,notify,但是notify不会立即释放锁,所以再让t1wait,此时t2继续运行,打印输出,并且notify唤醒t1。

    二、生产者消费者

    实现一个固定容量的容器,拥有put、get、getCount方法。能支持2个生产者线程和10个消费者进程的阻塞调用。

第一版代码
container:

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
class Container{
List list = new LinkedList();
public int count = 0;
public int maxSize = 10;

public synchronized void put(Object o) {
while (list.size() == maxSize) {
// while而不是if,当wait唤醒后,如果用if,会直接走到下面add然后count++。
// 如果此时其他的生产者先put,就会导致count++,此时轮到当前线程执行
// 还会再count++。所以用while来不断的判断count是否一定到了最大值。
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(o);
count++;
this.notifyAll();//唤醒所有消费者
}

public synchronized Object get() {
while (list.size() == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object t = list.remove(list.size() - 1);
count--;
this.notifyAll();// 唤醒所有生产者
return t;
}
}

main:

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
public class TestMain {
public static void main(String[] args) {
final Container container = new Container();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 5; j++) {
System.out.println(container.get());
}
}
},"consumer " + i).start();
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 25; j++) {
container.put(Thread.currentThread().getName()+" "+j);
}
}
},"producer "+ i).start();
}
}
}

第一版代码利用wait和notifyAll实现同步,当满了之后生产者wait,容器里面没东西,消费者wait。
注意这里面最后唤醒线程都用的是notifyAll,但是生产者和消费者都在同一个队列里面等着,notifyAll会同时通知所有人,而我们只想让消费者唤醒生产者,而不唤醒消费者,对于生产者也同理。
第二版

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
class Container2 {
List list = new LinkedList();
public int count = 0;
public int maxSize = 10;

private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();

public void put(Object o) {
lock.lock();
while (list.size() == maxSize) {
try {
producer.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(o);
count++;
consumer.signalAll();
lock.unlock();
}

public Object get() {
lock.lock();
while (list.size() == 0) {
try {
consumer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object o = list.remove(list.size() - 1);
count--;
producer.signalAll();
lock.unlock();
return o;
}
}

换用ReentrantLock实现同步。用Condition实现wait和notify,condition把原来的一个等待队列分为了两个,所以每次只需要notify对应的就行。