多种方法实现两线程轮流打印

news/2024/7/8 4:34:06 标签: 线程同步, 多线程, Semaphore, CyclicBarrier

题目:要求写个Java程序,一个线程专门打印a,另一个线程专门打印b,要求输出为轮流显示a和b,并重复50遍。

  1. CyclicBarrierCyclicBarrier允许一组线程互相等待,直到全部到达某个公共屏障点后,屏障才会打开,所有线程才会继续执行。
import java.util.concurrent.CyclicBarrier;

public class AB1 {
	public static void main(String[] args) {
		CyclicBarrier cb = new CyclicBarrier(2);
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				System.out.println("a");
				try {
					cb.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}, "ta").start();
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				while (cb.getNumberWaiting() < 1)
					;
				System.out.println("b");
				try {
					cb.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}, "tb").start();
	}

}
  1. 用CountDownLatch。CountDownLatch允许一个或多个线程等待其他线程完成操作后再执行。对于此题的情景应该不如CyclicBarrier适用,因为它不能循环使用。
import java.util.concurrent.CountDownLatch;

class AB2 {
	public static void main(String[] args) {
		CountDownLatch[] latch1 = {new CountDownLatch(1)};
		CountDownLatch[] latch2 = {new CountDownLatch(1)};
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				System.out.println("a");
				latch1[0].countDown();
				try {
					latch2[0].await();
					latch2[0] = new CountDownLatch(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "ta").start();
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				try {
					latch1[0].await();
					latch1[0] = new CountDownLatch(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("b");
				latch2[0].countDown();
			}
		}, "tb").start();
	}

}
  1. 信号量Semaphore, 个人推荐。也可以借鉴1中的用法。
import java.util.concurrent.Semaphore;

class AB3 {
	public static void main(String[] args) {
		Semaphore semaphoreB = new Semaphore(0);
		Semaphore semaphoreA = new Semaphore(0);
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				System.out.println("a");
				semaphoreB.release();
				try {
					semaphoreA.acquire();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "ta").start();
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				try {
					semaphoreB.acquire();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("b");
				semaphoreA.release();
			}
		}, "tb").start();

	}

}
  1. 直接使用AtomicInteger :
import java.util.concurrent.atomic.AtomicInteger;

class AB4 {
	public static void main(String[] args) {
		AtomicInteger lock = new AtomicInteger();
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				System.out.println("a");
				lock.incrementAndGet();
				while(lock.get() == 1)
					;
			}
		}, "ta").start();
		new Thread(() -> {
			for (int i = 0; i < 50; i++) {
				while(lock.get() < 1)
					;
				System.out.println("b");
				lock.getAndDecrement();
			}
		}, "tb").start();

	}

}
  1. 使用Thread.join()方法:
public class AB5 {
	public static void main(String[] args) {
		for (int i = 0; i < 50; i++) {
			Thread threadA = new Thread(() -> {
				System.out.println("a");
			}, "ta");
			threadA.start();
			try {
				threadA.join();
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			Thread threadB = new Thread(() -> {
				System.out.println("b");
			}, "tb");
			threadB.start();
			try {
				threadA.join();
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}
	}

}
  1. 使用synchronized及锁对象的wait/notify方法,或者使用Lock及其Condition的await/signal方法。这两种其实是我最先尝试的,但是失败了,直到参考了1。失败的原因是,当一个线程调用锁对象的notify方法(或者Condition的signal方法),另一个线程调用锁对象的wait方法(或者Condition的await方法),如果前者先于后者调用,那它实际上起不到我们期望的唤醒作用。所以成功的做法是干脆把所有wait(或者await)方法前置了。

class AB6 {
	public static void main(String[] args) {
		int[] num = { 0 };
		final Object lock = new Object();

		new Thread(() -> {
			synchronized (lock) {
				for (int i = 0; i < 50; ++i) {
					while (num[0] % 2 != 0) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("a");
					num[0]++;
					lock.notify();
				}
			}
		}, "ta").start();

		new Thread(() -> {
			synchronized (lock) {
				for (int i = 0; i < 50; ++i) {
					while (num[0] % 2 != 1) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("b");
					num[0]++;
					lock.notify();
				}
			}
		}, "tb").start();
	}

}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class AB7 {
	public static void main(String[] args) {
		int[] num = { 0 };
		final ReentrantLock lock = new ReentrantLock();
		Condition condition = lock.newCondition();

		new Thread(() -> {
			try {
				lock.lock();
				for (int i = 0; i < 50; ++i) {
					while (num[0] % 2 != 0) {
						condition.await();
					}
					System.out.println("a");
					num[0]++;
					condition.signal();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}, "ta").start();

		new Thread(() -> {
			try {
				lock.lock();
				for (int i = 0; i < 50; ++i) {
					while (num[0] % 2 != 1) {
						condition.await();
					}
					System.out.println("b");
					num[0]++;
					condition.signal();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				lock.unlock();
			}
		}, "tb").start();
	}

}

  1. 知乎-----面试官:请用五种方法实现多线程交替打印问题 ↩︎ ↩︎


http://www.niftyadmin.cn/n/1010320.html

相关文章

【C++初阶(二)】缺省参数以及函数重载

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C初阶之路⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 缺省参数&#xff06;函数重载 1. 前言2. 缺…

【已解决】Spring boot项目获取到resource目录下文件完整路径怎么获取 ?

在实际开发过程中&#xff0c;可能有时候&#xff0c;我们将文件放在resource下&#xff0c;在程序中需要获取到文件路径然后操作。比如&#xff0c;将公钥放到resource文件夹下&#xff0c;在程序中需要获取到这个公钥的完整路径。怎么操作&#xff1f; 需要访问的文件位置 获…

基于Tensorflow和Keras实现卷积神经网络CNN并进行猫狗识别

文章目录 一、环境配置1、安装Anaconda2、配置TensorFlow、Keras 二、猫狗数据集分类建模3.1 猫狗图像预处理3.2 猫狗分类的实例——基准模型3.1 构建神经网络3.2 配置优化器3.3 图片格式转化3.4 训练模型3.5 保存模型3.6 可视化 三、数据增强四、dropout 层五、参考资料 一、环…

【读书笔记】《月亮与六便士》- [英] 威廉·萨默塞特·毛姆 - 1919年出版

不停的阅读&#xff0c;然后形成自己的知识体系。 2023.07.03 读 一直听说毛姆的大名&#xff0c;却一直没有拜读。记得《小王子》中有读者提到这本书&#xff0c;看了眼作者竟然发现是毛姆。那么毫不犹豫的&#xff0c;赶紧拜读一番。 文章目录 作家榜推荐词第一章第二章第三…

【C++初阶】string类常见题目详解(二) —— 把字符串转换成整数、反转字符串、反转字符串 II、反转字符串中的单词 III、字符串相乘

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C初阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;C初阶】s…

ChatGPT Plugins内幕、源码及案例实战(三)

ChatGPT Plugins内幕、源码及案例实战(三) ChatGPT Plugins内幕、源码及案例实战 6.4 ChatGPT Retrieval Plugin全流程内幕解析 以ChatGPT检索插件为例,我们讲解一下它整个的流程,让大家有一个比较明确、清晰的统一认知:  数据存储:设立的前提是你有文档,会有一些文…

北大2019计算机学科夏令营上机考试

目录 A:数与字符串【找规律】 B:打印月历【暴力水题】 C:Hopscotch【BFS】 D:上楼梯【动态规划】 E:Life Line 【图】 F:跳蛙【DSP】 G:Falling Leaves【二叉搜索树】 H&#xff1a;昂贵的聘礼【图】 I:Connect【放弃】 A:数与字符串【找规律】 #include<iostream&…

针对WordPress程序无法升级最新版本的问题分析

WordPress程序是当前使用率最高的CMS系统之一&#xff0c;因开发功能完善&#xff0c;WordPress模板和插件众多而著称&#xff0c;茹莱神兽做三个网站&#xff0c;其中有两个网站使用的是WordPress程序搭建&#xff0c;可见它的受欢迎程度。 而WordPress程序本身也相当给力&a…