面试必问!Java 多线程中两个线程交替执行,一个输出偶数,一个输出奇数
前言
楼主今天在面经上看到这个题,挺有意思,小小的题目对多线程的考量还挺多。大部分同学都会使用 synchronized 来实现。楼主今天带来另外两种优化实现,让你面试的时候,傲视群雄!
第一种 synchronized
class ThreadPrintDemo2 {
public static void main(String[] args) {
final ThreadPrintDemo2 demo2 = new ThreadPrintDemo2();
Thread t1 = new Thread(demo2::print1);
Thread t2 = new Thread(demo2::print2);
t1.start();
t2.start();
}
public synchronized void print2() {
for (int i = 1; i <= 100; i += 2) {
System.out.println(i);
this.notify();
try {
this.wait();
Thread.sleep(100);// 防止打印速度过快导致混乱
} catch (InterruptedException e) {
// NO
}
}
}
public synchronized void print1() {
for (int i = 0; i <= 100; i += 2) {
System.out.println(i);
this.notify();
try {
this.wait();
Thread.sleep(100);// 防止打印速度过快导致混乱
} catch (InterruptedException e) {
// NO
}
}
}
}
通过 synchronized 同步两个方法,每次只能有一个线程进入,每打印一个数,就释放锁,另一个线程进入,拿到锁,打印,唤醒另一个线程,然后挂起自己。循环反复,实现了一个最基本的打印功能。
但,如果你这么写,面试官肯定是不满意的。楼主将介绍一种更好的实现。
使用 CAS 实现
public class ThreadPrintDemo {
static AtomicInteger cxsNum = new AtomicInteger(0);
static volatile boolean flag = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (; 100 > cxsNum.get(); ) {
if (!flag && (cxsNum.get() == 0 || cxsNum.incrementAndGet() % 2 == 0)) {
try {
Thread.sleep(100);// 防止打印速度过快导致混乱
} catch (InterruptedException e) {
//NO
}
System.out.println(cxsNum.get());
flag = true;
}
}
}
);
Thread t2 = new Thread(() -> {
for (; 100 > cxsNum.get(); ) {
if (flag && (cxsNum.incrementAndGet() % 2 != 0)) {
try {
Thread.sleep(100);// 防止打印速度过快导致混乱
} catch (InterruptedException e) {
//NO
}
System.out.println(cxsNum.get());
flag = false;
}
}
}
);
t1.start();
t2.start();
}
}
我们通过使用 CAS,避免线程的上下文切换,然后呢,使用一个 volatile 的 boolean 变量,保证不会出现可见性问题,记住,这个 flag 一定要是 volatile 的,如果不是,可能你的程序运行起来没问题,但最终一定会出问题,而且面试官会立马鄙视你。
这样就消除了使用 synchronized 导致的上下文切换带来的损耗,性能更好。相信,如果你面试的时候,这么写,面试官肯定很满意。
但,我们还有性能更好的。
使用 volatile
class ThreadPrintDemo3{
static volatile int num = 0;
static volatile boolean flag = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (; 100 > num; ) {
if (!flag && (num == 0 || ++num % 2 == 0)) {
try {
Thread.sleep(100);// 防止打印速度过快导致混乱
} catch (InterruptedException e) {
//NO
}
System.out.println(num);
flag = true;
}
}
}
);
Thread t2 = new Thread(() -> {
for (; 100 > num; ) {
if (flag && (++num % 2 != 0)) {
try {
Thread.sleep(100);// 防止打印速度过快导致混乱
} catch (InterruptedException e) {
//NO
}
System.out.println(num);
flag = false;
}
}
}
);
t1.start();
t2.start();
}
}
我们使用 volatile 变量代替 CAS 变量,减轻使用 CAS 的消耗,注意,这里 ++num 不是原子的,但不妨碍,因为有 flag 变量控制。而 num 必须是 volatile 的,如果不是,会导致可见性问题。
到这里,如果你面试的时候这么写,那么,offer 就不远啦!哈哈
面试必问!Java 多线程中两个线程交替执行,一个输出偶数,一个输出奇数的更多相关文章
- java面试必问:多线程的实现和同步机制,一文帮你搞定多线程编程
前言 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种资源和状态信息,包括打开的文件.子进程和信号处理. 线 ...
- Java并发 两个线程交替执行和死锁
今天看到一个题:两个线程交替打印奇数和偶数,即一个线程打印奇数,另一个打印偶数,交替打印从1到100.想了下有多重实现方法. wait和notify方法: public class OddEven { ...
- Java中高级面试必问之多线程TOP50(含答案)
以下为大家整理了今年一线大厂面试被问频率较高的多线程面试题,由于本人的见识局限性,所以可能不是很全面,也欢迎大家在后面留言补充,谢谢. 1.什么是线程? 2.什么是线程安全和线程不安全? 3.什么是自 ...
- [Java并发]实现两个线程交替打印奇偶数(volatile+yield实现)
解题思路 实现一个类OddEven 有一个打印奇数的方法,有一个打印偶数的方法. 类中有一个volatile变量 ,用来控制当前状态是该哪个方法打印. 方法中打印每个数前首先判断volatile变量的 ...
- 简单的线程同步问题:两个线程交替执行N次【Synchronized、Lock、ArrayBlockingQueue】
方法一:传统的线程方法import org.apache.log4j.Logger; /** * 两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象.<br/> * ...
- Java面试必问之Hashmap底层实现原理(JDK1.7)
1. 前言 Hashmap可以说是Java面试必问的,一般的面试题会问: Hashmap有哪些特性? Hashmap底层实现原理(get\put\resize) Hashmap怎么解决hash冲突? ...
- 一线大厂Java面试必问的2大类Tomcat调优
一.前言 最近整理了 Tomcat 调优这块,基本上面试必问,于是就花了点时间去搜集一下 Tomcat 调优都调了些什么,先记录一下调优手段,更多详细的原理和实现以后用到时候再来补充记录,下面就来介绍 ...
- Java面试必问之-JUC
JUC:java.util.concurrent (Java并发编程工具类) 代码:D:\JAVA\Java_Learning\Elipse_Project\workspace200301EE\JUC ...
- linux驱动工程面试必问知识点
linux内核原理面试必问(由易到难) 简单型 1:linux中内核空间及用户空间的区别?用户空间与内核通信方式有哪些? 2:linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化, ...
随机推荐
- zookeeper 分布式管理
分布式框架: Zookeeper与paxos算法 一. zookeeper是什么 官方说辞:Zookeeper 分布式服务框架是Apache Hadoop 的一个子项目,它主要是 ...
- AngularJs 学习笔记(四)服务
模型是指$scope上保存的包含瞬时状态数据的JavaScript对象. 服务是一个单例对象,只会被$injector实例化一次,并且是在需要的时候才会被创建,服务提供了把与特定功能相关联的方法集中在 ...
- amaze UI 如何添加原生表单验证
这段时间做的一个项目,整个系统就一个页面,然后就是各种模态框,js里拼HTML代码,而且因为表单空留距离小,最后选定了amaze ui原生的表单验证 在amaze ui官网找到 表单验证. 但是ama ...
- dp,px,pt,sp 的区别 以及dp 和 px 互转
dp = dip : device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA.HVGA和QVGA 推荐使用这个,不 ...
- Codeforces Round #479 (Div. 3) A. Wrong Subtraction
题目网址:http://codeforces.com/contest/977/problem/A 题解:给你一个数n,进行k次变换,从末尾开始-1,512变成511,511变成510,510会把0消掉 ...
- Floyd 算法求多源最短路径
Floyd算法: Floyd算法用来找出每对顶点之间的最短距离,它对图的要求是,既可以是无向图也可以是有向图,边权可以为负,但是不能存在负环(可根据最小环的正负来判定). 基本算法: Floyd算法基 ...
- Spring中IOC和AOP的理解
IOC和AOP是Spring的核心 IOC:控制反转:将创建对象以及维护对象之间的关系由代码交给了spring容器进行管理,也就是创建对象的方式反转了,交由spring容器进行管理. DI:依赖注入: ...
- 在C# 中 如何限制在文本框(textBox)中输入的类型为正整数
在文本框的 KeyPress 事件中写下这些代码就可以保证是正整数了 private void textBox1_KeyPress(object sender, KeyPressEventArgs e ...
- System Error. Code:1722. RPC服务器不可用解决办法
原文链接(转载请注明出处):System Error. Code:1722. RPC服务器不可用解决办法 问题 最近在软件设计上机课的时候,使用 starUML 建模工具画UML图的时候总是弹出一条如 ...
- C Primer Plus 第9章 函数 编程练习
复习题: 8. int choice(int a,int b,int c){ int max; max = a; if (b > max) max = b; if (c > max) ma ...