Java并发-交替打印的四种方法。
创始人
2024-03-13 18:30:29
0次

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 {// 唤醒线程blatch.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 {// 唤醒线程blatch.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);// 唤醒线程aconditionA.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 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();}
}

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...