面接は必ず聞きますJavaマルチスレッドでは、二つのスレッドが交互に実行され、一つは偶数、一つは奇数を出力します。

4173 ワード

前言
スレ主さんは今日面でこの問題を見ました。面白いです。小さなテーマはマルチスレッドに対する考えが多いです。ほとんどの学生は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同期の2つの方法により、毎回一つのスレッドだけが進入し、一つの数を印刷するごとにロックを解除し、もう一つのスレッドが進入し、ロックを取って、印刷して、もう一つのスレッドを起動し、自分を吊るします。繰り返して、最も基本的な印刷機能を実現しました。
しかし、このように書くと、面接官はきっと不満です。ビル主はより良い実現を紹介します。
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のbootlean変数を使って、可視性の問題がないことを保証します。覚えてください。このflagsは必ず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();

  }
}
私たちはCAS変数の代わりにvolatile変数を使って、CASを使った消耗を軽減します。ここで+numは原子ではないが、flags変数の制御があるので邪魔しないように注意してください。numはvolatileでなければなりません。そうでなければ、視認性の問題を引き起こします。
ここに来て、面接の時にこのように書いたら、オフは遠くないですよ。ははは?
タマゴは文字列をどうやって反転しますか?
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]);
    }

  }
}
これは比較的簡単です。二つの方法、一つはStringBuiderのreverse方法で、一つは配列に変換して自分で印刷します。自己転換性能がより良く、reverse方法の内部ステップがより多くなります。
はい、面接が成功しますように。
転載先:https://www.cnblogs.com/stateis0/p/9091254.html