1 前言
如下图所示,现在有两个线程A,B;A打印12345,B打印abcde,结果为1a2b3c4d5e交替输出。
1.1 采用wait和notify
【分析】我们要求线程A始终先打印,因此在线程B先获得CPU使用时间时也应该阻塞。
细节
- 线程A应该打印后,先notify,然后wait;
- 线程B先阻塞,然后打印后,先notity,再wait。
- 其线程B先阻塞的方法,采用countdownlatch实现
- 线程A在for循环后还需要在进行notify,因为线程B最后才能被打印。因此最后一定被阻塞。
package cn.itcast.n6.c1;
import java.util.concurrent.CountDownLatch;
/**
* @author : msf
* @date : 2022/12/2
* 交替输出
*/
public class AlteratePrint2 {
public static void main(String[] args) {
String numberA = "123456";
String characterB = "abcdef";
Object lock = new Object();
CountDownLatch latch = new CountDownLatch(1);
Thread threadA = new Thread(() -> {
synchronized (lock) {
for (char c : numberA.toCharArray()) {
System.out.print(c);
try {
// 唤醒线程b
latch.countDown();
lock.notify();
// 线程A阻塞
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 是为了更好的退出,因为程序在结束时候肯定会有一个线程在阻塞住。
lock.notify();
}
});
Thread threadB = new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
for (char c : characterB.toCharArray()) {
try {
System.out.print(c);
lock.notify();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
threadB.start();
threadA.start();
}
}
上述代码结果如下:
1.2 采用park 和unpark
package cn.itcast.n6.c1;
import java.util.concurrent.locks.LockSupport;
/**
* @author : msf
* @date : 2022/12/2
* 交替输出
*/
public class AlteratePrint {
static Thread threadA = null;
static Thread threadB = null;
public static void main(String[] args) {
String numberA = "123456";
String characterB = "abcdef";
threadA = new Thread(()->{
for (char c : numberA.toCharArray()) {
System.out.print(c);
LockSupport.unpark(threadB);
LockSupport.park();
}
});
threadB = new Thread(()->{
for (char c : characterB.toCharArray()) {
LockSupport.park();
System.out.print(c);
LockSupport.unpark(threadA);
}
});
threadA.start();
threadB.start();
}
}
上述代码结果如下:
1.3 采用lock锁和condition
package cn.itcast.n6.c1;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author : msf
* @date : 2022/12/2
* 交替输出
*/
public class AlteratePrint3 {
public static void main(String[] args) {
String numberA = "123456";
String characterB = "abcdef";
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
CountDownLatch latch = new CountDownLatch(1);
Thread threadA = new Thread(() -> {
lock.lock();
try {
for (char c : numberA.toCharArray()) {
System.out.print(c);
try {
// 唤醒线程b
latch.countDown();
conditionB.signal();
// 线程A阻塞
conditionA.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
conditionB.signal();
} finally {
lock.unlock();
}
});
Thread threadB = new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
for (char c : characterB.toCharArray()) {
try {
System.out.print(c);
// 唤醒线程a
conditionA.signal();
// 线程b阻塞
conditionB.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
});
threadB.start();
threadA.start();
}
}
上述代码结果如下:
1.4 采用transferQueue实现
为什么可以用transferQueue实现呢?
原因
其的take 和 transfer命令都是同步阻塞的。
package cn.itcast.n6.c1;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author : msf
* @date : 2022/12/2
* 交替输出
*/
public class AlteratePrint4 {
public static void main(String[] args) {
String numberA = "123456";
String characterB = "abcdef";
TransferQueue<Character> queue = new LinkedTransferQueue<>();
Thread threadA = new Thread(() -> {
try {
for (char c : numberA.toCharArray()) {
queue.transfer(c);
System.out.print(queue.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
try {
for (char c : characterB.toCharArray()) {
System.out.print(queue.take());
queue.transfer(c);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
threadA.start();
}
}