前言

楼主今天在面经上看到这个题,挺有意思,小小的题目对多线程的考量还挺多。大部分同学都会使用 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 多线程中两个线程交替执行,一个输出偶数,一个输出奇数的更多相关文章

  1. java面试必问:多线程的实现和同步机制,一文帮你搞定多线程编程

    前言 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种资源和状态信息,包括打开的文件.子进程和信号处理. 线 ...

  2. Java并发 两个线程交替执行和死锁

    今天看到一个题:两个线程交替打印奇数和偶数,即一个线程打印奇数,另一个打印偶数,交替打印从1到100.想了下有多重实现方法. wait和notify方法: public class OddEven { ...

  3. Java中高级面试必问之多线程TOP50(含答案)

    以下为大家整理了今年一线大厂面试被问频率较高的多线程面试题,由于本人的见识局限性,所以可能不是很全面,也欢迎大家在后面留言补充,谢谢. 1.什么是线程? 2.什么是线程安全和线程不安全? 3.什么是自 ...

  4. [Java并发]实现两个线程交替打印奇偶数(volatile+yield实现)

    解题思路 实现一个类OddEven 有一个打印奇数的方法,有一个打印偶数的方法. 类中有一个volatile变量 ,用来控制当前状态是该哪个方法打印. 方法中打印每个数前首先判断volatile变量的 ...

  5. 简单的线程同步问题:两个线程交替执行N次【Synchronized、Lock、ArrayBlockingQueue】

    方法一:传统的线程方法import org.apache.log4j.Logger; /** * 两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象.<br/> * ...

  6. Java面试必问之Hashmap底层实现原理(JDK1.7)

    1. 前言 Hashmap可以说是Java面试必问的,一般的面试题会问: Hashmap有哪些特性? Hashmap底层实现原理(get\put\resize) Hashmap怎么解决hash冲突? ...

  7. 一线大厂Java面试必问的2大类Tomcat调优

    一.前言 最近整理了 Tomcat 调优这块,基本上面试必问,于是就花了点时间去搜集一下 Tomcat 调优都调了些什么,先记录一下调优手段,更多详细的原理和实现以后用到时候再来补充记录,下面就来介绍 ...

  8. Java面试必问之-JUC

    JUC:java.util.concurrent (Java并发编程工具类) 代码:D:\JAVA\Java_Learning\Elipse_Project\workspace200301EE\JUC ...

  9. linux驱动工程面试必问知识点

    linux内核原理面试必问(由易到难) 简单型 1:linux中内核空间及用户空间的区别?用户空间与内核通信方式有哪些? 2:linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化, ...

随机推荐

  1. zookeeper 分布式管理

    分布式框架: Zookeeper与paxos算法 一.            zookeeper是什么 官方说辞:Zookeeper 分布式服务框架是Apache Hadoop 的一个子项目,它主要是 ...

  2. AngularJs 学习笔记(四)服务

    模型是指$scope上保存的包含瞬时状态数据的JavaScript对象. 服务是一个单例对象,只会被$injector实例化一次,并且是在需要的时候才会被创建,服务提供了把与特定功能相关联的方法集中在 ...

  3. amaze UI 如何添加原生表单验证

    这段时间做的一个项目,整个系统就一个页面,然后就是各种模态框,js里拼HTML代码,而且因为表单空留距离小,最后选定了amaze ui原生的表单验证 在amaze ui官网找到 表单验证. 但是ama ...

  4. dp,px,pt,sp 的区别 以及dp 和 px 互转

    dp = dip : device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA.HVGA和QVGA 推荐使用这个,不 ...

  5. Codeforces Round #479 (Div. 3) A. Wrong Subtraction

    题目网址:http://codeforces.com/contest/977/problem/A 题解:给你一个数n,进行k次变换,从末尾开始-1,512变成511,511变成510,510会把0消掉 ...

  6. Floyd 算法求多源最短路径

    Floyd算法: Floyd算法用来找出每对顶点之间的最短距离,它对图的要求是,既可以是无向图也可以是有向图,边权可以为负,但是不能存在负环(可根据最小环的正负来判定). 基本算法: Floyd算法基 ...

  7. Spring中IOC和AOP的理解

    IOC和AOP是Spring的核心 IOC:控制反转:将创建对象以及维护对象之间的关系由代码交给了spring容器进行管理,也就是创建对象的方式反转了,交由spring容器进行管理. DI:依赖注入: ...

  8. 在C# 中 如何限制在文本框(textBox)中输入的类型为正整数

    在文本框的 KeyPress 事件中写下这些代码就可以保证是正整数了 private void textBox1_KeyPress(object sender, KeyPressEventArgs e ...

  9. System Error. Code:1722. RPC服务器不可用解决办法

    原文链接(转载请注明出处):System Error. Code:1722. RPC服务器不可用解决办法 问题 最近在软件设计上机课的时候,使用 starUML 建模工具画UML图的时候总是弹出一条如 ...

  10. 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 ...