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 方法内部步驟更多。
好啦,希望大家面試成功!!