面试必问!Java 多线程中两个线程交替执行,一个输出偶数,一个输出奇数
2018-05-26 00:55
627 查看
前言
楼主今天在面经上看到这个题,挺有意思,小小的题目对多线程的考量还挺多。大部分同学都会使用 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 就不远啦!哈哈😆!!
彩蛋 如何翻转字符串?
class ReverseDemo { public static void main(String[] args) { String test = "abcdefg"; System.out.println(new StringBuilder(test).reverse()); char[] arr = test.toCharArray(); for (int i = arr.length - 1; i >= 0; i--) { System.out.print(arr[i]); } } }
这个就比较简单了,两种方式,一个是 StringBuilder 的 reverse 方法,一个是转换成数组自己打印。自己转换性能更好,reverse 方法内部步骤更多。
好啦,希望大家面试成功!!
相关文章推荐
- Java 多线程中。两个线程交替执行,一个输出偶数,一个输出奇数(方法二)
- Java 多线程中。两个线程交替执行,一个输出偶数,一个输出奇数(方法一)
- Java 多线程中。两个线程交替执行,一个输出偶数,一个输出奇数
- java两个线程轮流输出奇数和偶数到100
- Java 多线程实现的三种方法,附两个线程执行不同的输出
- JAVA多线程之两个线程同时写一个文件
- 《Java面试试题》写一个两个线程之间的死锁
- 实现两个线程A,B,A打印5个奇数,B打印5个偶数,交替进行,如此循环50次
- java两个线程交替执行
- 每天一道算法题7 查找链表中倒数第k个结点 ; 输入一个单向链表。如果该链表的结点数为奇数,输出中间的结点;如果链表结点数为偶数,输出中间两个结点前面的一个
- 【多线程】两个线程 交替执行
- java中多线程一个线程负责客户端读数据到队列另外一个线程负责从队列输出数据
- [Java 09 多线程] 线程是指一个进程在执行过程中可以产生更小的程序单元
- 用Java写一个多线程程序,如写四个线程,二个加1,二个对一变量减一,输出:
- 多线程技术: 两个线程交替打印奇数和偶数
- 设计一个算法,将计算机产生的n个随机数,分为奇数、偶数两组,并将它们分别压入两个栈中,然后输出在屏幕上
- java多线程问题,线程交替执行
- Java 多线程 子线程 交替打印 奇偶数
- 多线程技术: 两个线程交替打印奇数和偶数
- Java两个线程轮换打印1-100内的奇数和偶数