线程交替输出问题
面试题:
用两个线程,一个输出字母,一个输出数字,交替输出1A2B3C…26Z
方法一:wait_notify
public class OutputByTurns2 {
public static void main(String[] args) throws Exception {
final Object o = new Object();
int a = (int) 'a';
CountDownLatch countDownLatch = new CountDownLatch(1); // 控制执行顺序
new Thread(() -> {
// method
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
for (int i = 0; i < 26; i++) {
System.out.println((char) (a + i));
try {
o.notify();
o.wait();// 让出锁 持有该对象锁的线程去wait()
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "字母").start();
new Thread(() -> {
// method
synchronized (o) {
countDownLatch.countDown();
for (int i = 0; i < 26; i++) {
System.out.println(i + 1);
try {
o.notify(); // 随机唤醒一个 wait 线程
o.wait();// 让出锁 持有该对象锁的线程去wait()
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify(); // ⚠️ 不然程序停止不了,一直互相通知
}
}, "数字").start();
}
}
方法二:LockSupport
public class OutputByTurns {
static Thread t1,t2 = null;
public static void main(String[] args) {
int a = (int) 'a';
t1 = new Thread(() -> {
// method
for (int i=0;i<26;i++) {
System.out.println(i+1);
LockSupport.unpark(t2); // 叫醒t2
LockSupport.park(); // t1 阻塞
}
}, "t1");
t2 = new Thread(() -> {
// method
for (int i=0;i<26;i++) {
LockSupport.park(); // t2 阻塞
System.out.println((char) (a+i));
LockSupport.unpark(t1); // 叫醒t1
}
}, "t2");
t1.start();
t2.start();
}
}
方法三:ReentrantLock
资源类写法 🤔
class ShareData {
private int flag = 1; // 0:打印字母 1:打印数字
private Lock lock = new ReentrantLock();
private Condition conditionNumber = lock.newCondition();
private Condition conditionLetter = lock.newCondition();
public void printNumber(int number) {
lock.lock();
try {
// 1 判断
while (flag != 1) {
conditionNumber.await();
}
// 2 干活
System.out.println(number);
// 3 通知
flag = 0;
conditionLetter.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printLetter(char ch) {
lock.lock();
try {
// 1 判断
while (flag != 0) {
conditionLetter.await();
}
// 2 干活
System.out.println(ch);
// 3 通知
flag = 1;
conditionNumber.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class OutputByTurns3_2 {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(() -> {
// method
for (int i = 0; i < 26; i++) {
shareData.printNumber(i+1);
}
}, "打印数字").start();
new Thread(() -> {
// method
for (int i = 0; i < 26; i++) {
shareData.printLetter((char)('a'+i));
}
}, "打印字母").start();
}
}
CountDownLatch 控制顺序
public class OutputByTurns3 {
public static void main(String[] args) {
int a = (int) 'a';
Lock lock = new ReentrantLock();
CountDownLatch countDownLatch = new CountDownLatch(1); // 控制执行顺序
// ⚠️ ReentrantLock可以指定唤醒哪一个队列中的线程
Condition conditionNumber = lock.newCondition(); // 数字条件队列
Condition conditionLetter = lock.newCondition(); // 字母条件队列
new Thread(() -> {
// method
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
// ... method body
for (int i = 0; i < 26; i++) {
System.out.println((char) (a + i));
conditionNumber.signal();
conditionLetter.await();
}
conditionNumber.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "字母").start();
new Thread(() -> {
// method
countDownLatch.countDown();
lock.lock();
try {
// ... method body
for (int i = 0; i < 26; i++) {
System.out.println(i + 1);
conditionLetter.signal();
conditionNumber.await();
}
conditionLetter.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "数字").start();
}
}
方法四:TransferQueue阻塞队列
public class OutputByTurns4 {
public static void main(String[] args) {
int a = (int) 'a';
// 谁transfer进去一个元素谁阻塞,需要别的线程拿走才能被唤醒
// take的时候为空则自己入队等待,不为空则不阻塞
// 若入队时发现有一个元素为null的节点,直接就将元素填充到该节点,并唤醒该节点等待的线程
// ⚠️ 谁握着元素谁阻塞
TransferQueue<String> queue = new LinkedTransferQueue<>();
new Thread(() -> {
// method
for (int i = 0; i < 26; i++) {
try {
System.out.println(queue.take());
queue.transfer(String.valueOf((char)(a+i)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "字母").start();
new Thread(() -> {
// method
for (int i = 0; i < 26; i++) {
try {
queue.transfer(String.valueOf(i+1));
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "数字").start();
}
}
既已览卷至此,何不品评一二: