两个线程如何交替执行,一个输出偶数一个输出奇数?

简介: 楼主今天在面经上看到这个题,挺有意思,小小的题目对多线程的考量还挺多。大部分同学都会使用 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 同步两个方法,每次只能有一个线程进入,每打印一个数,就释放锁,另一个线程进入,拿到锁,打印,唤醒另一个线程,然后挂起自己。循环反复,实现了一个最基本的打印功能。面试常问的:Synchronized 有几种用法。


但,如果你这么写,面试官肯定是不满意的。楼主将介绍一种更好的实现。

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 的,如果不是,可能你的程序运行起来没问题,但最终一定会出问题,而且面试官会立马鄙视你。面试常问的:深入理解CAS算法原理。


这样就消除了使用 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();
  }
}

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

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

目录
相关文章
高频面试题:如何分别用三种姿势实现三个线程交替打印0到100
高频面试题:如何分别用三种姿势实现三个线程交替打印0到100
525 0
|
消息中间件 移动开发 自然语言处理
多线程知识:三个线程如何交替打印ABC循环100次
synchronized是Java中的一个关键字,用于实现对共享资源的互斥访问。wait和notify是Object类中的两个方法,用于实现线程间的通信。wait方法会让当前线程释放锁,并进入等待状态,直到被其他线程唤醒。notify方法会唤醒一个在同一个锁上等待的线程。
206 1
【多线程】两个线程轮流打印数字1-100,一个打奇数一个打偶数,顺序打印
【多线程】两个线程轮流打印数字1-100,一个打奇数一个打偶数,顺序打印
一个有意思的面试题 → 线程交替输出问题
用两个线程,一个输出数字,一个输出字母,交替输出 1A2B3C4D...26Z
三个线程交替打印ABC100次问题思考之二,使用信号量的优雅实现
三个线程交替打印ABC100次问题思考之二,使用信号量的优雅实现
144 0
三个线程交替打印ABC100次问题思考
三个线程交替打印ABC100次问题思考
178 0
斐讯面试记录—三线程交替打印ABC
斐讯面试记录—三线程交替打印ABC
83 0
一个有意思的面试题 → 线程交替输出问题
问题描述   用两个线程,一个输出数字,一个输出字母,交替输出 1A2B3C4D...26Z
Java老掉牙的面试问题:线程交替打印问题,分析实操一下 下
Java老掉牙的面试问题:线程交替打印问题,分析实操一下 下