两个线程如何交替执行,一个输出偶数一个输出奇数?
发布日期:2021-06-30 12:45:54 浏览次数:3 分类:技术文章

本文共 3417 字,大约阅读时间需要 11 分钟。

作者:莫那·鲁道 cnblogs.com/stateis0/p/9091254.html

楼主今天在面经上看到这个题,挺有意思,小小的题目对多线程的考量还挺多。大部分同学都会使用 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) {      }    }  }  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) {      }    }  }}

通过 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) {          }          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) {          }          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) {          }          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) {          }          System.out.println(num);          flag = false;        }      }    }    );    t1.start();    t2.start();  }}

我们使用  变量代替 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 方法内部步骤更多。

好啦,希望大家面试成功!!

推荐去我的博客阅读更多:

1.

2.

3.

4.

觉得不错,别忘了点赞+转发哦!

转载地址:https://javastack.blog.csdn.net/article/details/106069888 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Dubbo服务调试管理实用命令
下一篇:面试再问值传递与引用传递,把这篇文章砸给他!

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年05月06日 00时26分31秒