4个线程,一个打印A,一个打印B,一个打印C,一个打印D,要求按 ABCDABCD 的顺序打印
多线程同步,首先想到的就是 synchronized 和 wait notify 机制。
import java.util.concurrent.CountDownLatch;
public class Main {
final static int N = 4;
static int nextNum = 1;
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object o4 = new Object();
CountDownLatch countDownLatch = new CountDownLatch(4);
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o1) {
while (nextNum != 1) {
try {
// System.out.println("o1 wait");
o1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("A");
synchronized (o2) {
nextNum = 2;
// System.out.println("o2 notifyAll");
o2.notifyAll();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o2) {
while (nextNum != 2) {
try {
// System.out.println("o2 wait");
o2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("B");
synchronized (o3) {
nextNum = 3;
// System.out.println("o3 notifyAll");
o3.notifyAll();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o3) {
while (nextNum != 3) {
try {
// System.out.println("o3 wait");
o3.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (o4) {
System.out.println("C");
nextNum = 4;
// System.out.println("o4 notifyAll");
o4.notifyAll();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o4) {
while (nextNum != 4) {
try {
// System.out.println("o4 wait");
o4.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (o1) {
System.out.println("D");
nextNum = 1;
// System.out.println("o1 notifyAll");
o1.notifyAll();
}
}
countDownLatch.countDown();
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4 个线程,根据条件分别阻塞在不同的 object 锁上
我只用一个 Object 锁可以吗???当然可以只用一个锁,4个线程同时竞争一个锁,然后同时唤醒再竞争,再附加一个条件变量就行了。代码如下:
import java.util.concurrent.CountDownLatch;
public class Main {
final static int N = 4;
static int nextNum = 1;
public static void main(String[] args) {
Object o1 = new Object();
CountDownLatch countDownLatch = new CountDownLatch(4);
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o1) {
while (nextNum != 1) {
try {
o1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("A");
nextNum = 2;
o1.notifyAll();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o1) {
while (nextNum != 2) {
try {
o1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("B");
nextNum = 3;
o1.notifyAll();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o1) {
while (nextNum != 3) {
try {
// System.out.println("o3 wait");
o1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
nextNum = 4;
o1.notifyAll();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
synchronized (o1) {
while (nextNum != 4) {
try {
o1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("D");
nextNum = 1;
o1.notifyAll();
}
}
countDownLatch.countDown();
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
notifyAll 会唤醒所有等待线程,所有线程再同时竞争锁。notify 会随机唤醒一个等待线程。随机唤醒一个线程,被唤醒的线程条件不一定满足,又会进入等待,在高并发场景下性能不好。这就是为什么又出现了 Condition 机制。
我用 synchronized 方法可以吗???当然可以,synchronized 方法默认会锁住当前对象,就不用 new 一个对象锁了。
下面看看 Condition 实现
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
final static int N = 4;
static int nextNum = 1;
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
Condition condition4 = lock.newCondition();
CountDownLatch countDownLatch = new CountDownLatch(4);
new Thread(() -> {
for (int i = 0; i < N; i++) {
lock.lock();
try {
while (nextNum != 1) {
try {
// System.out.println("condition1 await");
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("A");
nextNum = 2;
// System.out.println("condition2 signalAll");
condition2.signalAll();
} finally {
lock.unlock();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
lock.lock();
try {
while (nextNum != 2) {
try {
// System.out.println("condition2 await");
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("B");
nextNum = 3;
// System.out.println("condition3 signalAll");
condition3.signalAll();
} finally {
lock.unlock();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
lock.lock();
try {
while (nextNum != 3) {
try {
// System.out.println("condition3 await");
condition3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
nextNum = 4;
// System.out.println("condition4 signalAll");
condition4.signalAll();
} finally {
lock.unlock();
}
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
lock.lock();
try {
while (nextNum != 4) {
try {
// System.out.println("condition4 await");
condition4.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("D");
nextNum = 1;
// System.out.println("condition1 signalAll");
condition1.signalAll();
} finally {
lock.unlock();
}
}
countDownLatch.countDown();
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ReentrantLock 就等价于 Object 锁。condition.await() 等价于 object.wait(), condition.signal() 等价于 object.notify()。
我可以只用一个条件变量吗???当然可以,那样和上面只用一个对象锁没什么区别。
一个ReentrantLock 绑定多个 Condition 对象意味着什么???为什么用 Condition 写生产者消费者效率高???
synchronized方法 或 Object锁 都只有一个等待队列,所有阻塞的线程都挂在当前对象 或一个 object 实例上。 notify 会随机唤醒队列中的一个线程,而唤醒的线程不一定满足条件,所以又进入等待,这样效率低了一点。
而 ReentrantLock 绑定多个 Condition 对象, 每个 Condition 对象上都有一个等待队列,我们可以只唤醒一个Condition 对象上等待的线程 。也就是 Condition 能实现粒度更细的锁。
看别人写的信号量机制
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
public class Main {
final static int N = 4;
public static void main(String[] args) {
Semaphore semaphore1 = new Semaphore(1);
Semaphore semaphore2 = new Semaphore(0);
Semaphore semaphore3 = new Semaphore(0);
Semaphore semaphore4 = new Semaphore(0);
CountDownLatch countDownLatch = new CountDownLatch(4);
new Thread(() -> {
for (int i = 0; i < N; i++) {
try {
semaphore1.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A");
semaphore2.release();
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
try {
semaphore2.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
semaphore3.release();
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
try {
semaphore3.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C");
semaphore4.release();
}
countDownLatch.countDown();
}).start();
new Thread(() -> {
for (int i = 0; i < N; i++) {
try {
semaphore4.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("D");
semaphore1.release();
}
countDownLatch.countDown();
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}