面试必问,如何控制多个线程的执行顺序
生活随笔
收集整理的這篇文章主要介紹了
面试必问,如何控制多个线程的执行顺序
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
面試中經常被遇到一個問題:現在有三個線程,分別執行會打印A,B,C,如何編碼使得三個線程順序執行,即輸出‘ABCABC’,這道題看似簡單,但是如果對多線程不熟悉或者沒有準備還真的不好回答。
下面列舉幾種比較好理解的方法,代碼手工編寫,都已測試
lock和Synchronized都是通過線程間的通信,喚醒指定的線程實現順序執行
1.使用lock
public class Task2Lock {static Lock lock = new ReentrantLock();static int count = 0;static Condition condition1 = lock.newCondition();static Condition condition2 = lock.newCondition();static Condition condition3 = lock.newCondition();/*** 下面的while只是為了控制打印的次數* @param args*/public static void main(String[] args) {Thread tA = new Thread(() -> {while (count < 4) {//先獲取鎖lock.lock();//如果count取余3不等于0,就awaitif (count % 3 != 0) {try {condition1.await();} catch (InterruptedException e) {e.printStackTrace();}}//否則就執行任務并count++,喚醒線程2System.out.println("a");count++;condition2.signal();lock.unlock();}});Thread tB = new Thread(() -> {while (count < 4) {lock.lock();if (count % 3 != 1) {try {condition2.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("l");count++;condition3.signal();lock.unlock();}});Thread tC = new Thread(() -> {while (count < 4) {lock.lock();if (count % 3 != 2) {try {condition3.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("i");count++;condition1.signal();lock.unlock();}});tA.start();tB.start();tC.start();} }2.使用Synchronized
static Object object = new Object();static int count = 1;public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 2; i++) {synchronized (object) {//注意這里要用while循環while (count != 1) {try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("a");count = 2;object.notifyAll();}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 2; i++) {synchronized (object) {while (count != 2) {try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("b");count = 3;object.notifyAll();}}});Thread t3 = new Thread(() -> {for (int i = 0; i < 2; i++) {synchronized (object) {while (count != 3) {try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("c");count = 1;object.notifyAll();}}});t1.start();t2.start();t3.start();}還有下面這種寫法,其實就是鎖對象換成了this,我更喜歡上面的,邏輯清晰
public class TaskSynchronized {public static void main(String[] args) {PrintService printService = new PrintService();Thread thread1 = new Thread(() -> {for (int i = 0; i < 2; i++) {printService.printA();}});thread1.setName("A");Thread thread2 = new Thread(() -> {for (int i = 0; i < 2; i++) {printService.printB();}});thread2.setName("B");Thread thread3 = new Thread(() -> {for (int i = 0; i < 2; i++) {printService.printC();}});thread3.setName("C");thread2.start();thread3.start();thread1.start();}/*** 里面的printA,printB,printC都必須加synchronized修飾*/static class PrintService {private int flag = 1;public synchronized void printA() {while (flag != 1) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(Thread.currentThread().getName());flag = 2;this.notifyAll();}public synchronized void printB() {while (flag != 2) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(Thread.currentThread().getName());flag = 3;this.notifyAll();}public synchronized void printC() {while (flag != 3) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(Thread.currentThread().getName());flag = 1;this.notifyAll();}} }3. 使用join
下面的代碼只能輸出一次ABC,暫時還沒想到如何輸出多次
public class TaskJoin {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(()->{System.out.println("a");});Thread thread2 = new Thread(()->{System.out.println("b");});Thread thread3 = new Thread(()->{System.out.println("c");});//join方法會使得調用線程等待直到thread1結束diethread1.start();thread1.join();thread2.start();thread2.join();thread3.start();} }join的作用是當前線程等待這個線程結束,底層實現就是循環判斷isAlive(),不斷的await。isAlive()是調用的系統本地方法
public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}4.使用SingleThreadExecutor
public class Task2SingleThreadExecutor {//利用并發包里的Excutors的newSingleThreadExecutor產生一個單線程的線程池,// 而這個線程池的底層原理就是設置核心線程和最大線程都是1static ExecutorService executorService = Executors.newSingleThreadExecutor();public static void main(String[] args) {Thread thread1 = new Thread(()->{System.out.println("a");});Thread thread2 = new Thread(()->{System.out.println("b");});Thread thread3 = new Thread(()->{System.out.println("c");});for (int i = 0; i < 2; i++) {executorService.submit(thread1);executorService.submit(thread2);executorService.submit(thread3);}//作用是關閉線程池,終止線程執行executorService.shutdown();} }SingleThreadExecutor底層其實就是創建一個核心線程池為1,無界隊列,最大線程為1的線程池
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}5.使用信號量Semaphore
public class Task2Semaphore {static Semaphore semaphore1 = new Semaphore(1);static Semaphore semaphore2 = new Semaphore(1);static Semaphore semaphore3 = new Semaphore(1);static int count = 0;public static void main(String[] args) {Thread thread1 = new Thread(()->{while (count < 4){try {semaphore1.acquire();} catch (InterruptedException e) {e.printStackTrace();}//這里判斷一次就可以控制執行的次數 System.out.println("a");count++;semaphore2.release();}});Thread thread2 = new Thread(()->{while (count < 4){try {//acquire方法會一直阻塞直到另一個線程釋放他semaphore2.acquire();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("b");count++;semaphore3.release();}});Thread thread3 = new Thread(()->{while (count < 4){try {semaphore3.acquire();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("c");count++;semaphore1.release();}});try {//這里必須先獲取semaphore2,semaphore3,阻止線程2,3運行。也就是必須有線程1先執行,并釋放2的資源semaphore2.acquire();semaphore3.acquire();} catch (InterruptedException e) {e.printStackTrace();}thread2.start();thread1.start();thread3.start();} }總結
以上是生活随笔為你收集整理的面试必问,如何控制多个线程的执行顺序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM调优技巧与经验
- 下一篇: 并发工具概览