java处理并发时,synchronized语句块中,无论使用对象监视器的wait notify/notifyAll还是Condition的await signal/ signalAll方法调用,我们首先都会对共享数据的临界值进行判断,当条件满足或者不满足的时候才会调用相关方法使得当前线程挂起,或者唤醒wait的线程。
1. wait/notify示例代码
package org.example.model.guava;
import lombok.SneakyThrows;
import org.junit.Test;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;
public class MonitorTest {
// queue既是队列又是并发监视器
private LinkedList<Long> queue = new LinkedList<>();
private int maxSize = 2;
@Test
public void test() {
AtomicLong count = new AtomicLong(0);
// 起三个线程向队列添加元素
IntStream.range(0, 3).forEach(i ->
new Thread(() -> {
while (true) {
long value = count.getAndIncrement();
offer(value);
System.out.println("offer--" + i + ":" + value);
sleep(1);
}
}).start());
// 起三个线程从队列中取元素
IntStream.range(0, 3).forEach(i ->
new Thread(() -> {
while (true) {
long value = take();
System.out.println("take---" + i + ":" + value);
sleep(1);
}
}).start());
sleep(100);
}
// 向队列中放入值
@SneakyThrows
private void offer(long value) {
synchronized (queue) {
// 队列满,则wait
while (queue.size() >= maxSize) {
queue.wait();
}
queue.addLast(value);
// 唤醒所有等待的线程
queue.notifyAll();
}
}
// 从队列中取值
@SneakyThrows
private long take() {
synchronized (queue) {
// 队列空,则wait
while (queue.isEmpty()) {
queue.wait();
}
Long value = queue.removeFirst();
// 唤醒所有等待的线程
queue.notifyAll();
return value;
}
}
@SneakyThrows
private void sleep(long delay) {
Thread.sleep(delay);
}
}
2. Condition的await signal/ signalAll示例
package org.example.model.guava;
import lombok.SneakyThrows;
import org.junit.Test;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;
public class MonitorTest {
private LinkedList<Long> queue = new LinkedList<>();
// 可重入锁,并创建两个条件,队列空和队列满
private ReentrantLock lock = new ReentrantLock();
private Condition emptyCondition = lock.newCondition();
private Condition fullCondition = lock.newCondition();
private int maxSize = 2;
@Test
public void test() {
AtomicLong count = new AtomicLong(0);
// 起三个线程向队列添加元素
IntStream.range(0, 3).forEach(i ->
new Thread(() -> {
while (true) {
long value = count.getAndIncrement();
offer(value);
System.out.println("offer--" + i + ":" + value);
sleep(1);
}
}).start());
// 起三个线程从队列中取元素
IntStream.range(0, 3).forEach(i ->
new Thread(() -> {
while (true) {
long value = take();
System.out.println("take---" + i + ":" + value);
sleep(1);
}
}).start());
sleep(100);
}
// 向队列中放入值
@SneakyThrows
private void offer(long value) {
try {
lock.lock();
// 队列满,使用fullCondition进入等待
while (queue.size() >= maxSize) {
fullCondition.await();
}
queue.addLast(value);
// 唤醒emptyCondition阻塞的线程
emptyCondition.signalAll();
} finally {
lock.unlock();
}
}
// 从队列中取值
@SneakyThrows
private long take() {
try {
lock.lock();
// 队列空,使用emptyCondition进入等待
while (queue.isEmpty()) {
emptyCondition.await();
}
Long value = queue.removeFirst();
// 唤醒fullCondition阻塞的线程
fullCondition.signalAll();
return value;
} finally {
lock.unlock();
}
}
@SneakyThrows
private void sleep(long delay) {
Thread.sleep(delay);
}
}
3. 使用guava monitor
我们发现无论时wait/notify,还是使用锁的condition的await/signal,都需要在代码中判断满足条件时编写wait/notify, await/signal代码,代码意义不明显,可读性比较差。使用guava monitor能够使得代码的语义性更强,具有更好的可读性。示例如下:
package org.example.model.guava;
import com.google.common.util.concurrent.Monitor;
import lombok.SneakyThrows;
import org.junit.Test;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;
public class MonitorTest {
private LinkedList<Long> queue = new LinkedList<>();
private int maxSize = 2;
private Monitor monitor = new Monitor();
private Monitor.Guard canOffer = monitor.newGuard(() -> queue.size() <= maxSize);
private Monitor.Guard canTake = monitor.newGuard(() -> !queue.isEmpty());
@Test
public void test() {
AtomicLong count = new AtomicLong(0);
// 起三个线程向队列添加元素
IntStream.range(0, 3).forEach(i ->
new Thread(() -> {
while (true) {
long value = count.getAndIncrement();
offer(value);
System.out.println("offer--" + i + ":" + value);
sleep(1);
}
}).start());
// 起三个线程从队列中取元素
IntStream.range(0, 3).forEach(i ->
new Thread(() -> {
while (true) {
long value = take();
System.out.println("take---" + i + ":" + value);
sleep(1);
}
}).start());
sleep(100);
}
// 向队列中放入值
@SneakyThrows
private void offer(long value) {
try {
monitor.enterWhen(canOffer);
queue.addLast(value);
} finally {
monitor.leave();
}
}
// 从队列中取值
@SneakyThrows
private long take() {
try {
monitor.enterWhen(canTake);
return queue.removeFirst();
} finally {
monitor.leave();
}
}
@SneakyThrows
private void sleep(long delay) {
Thread.sleep(delay);
}
}