面试必问,如何控制多个线程的执行顺序
生活随笔
收集整理的這篇文章主要介紹了
面试必问,如何控制多个线程的执行顺序
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
面試中經(jīng)常被遇到一個(gè)問(wèn)題:現(xiàn)在有三個(gè)線程,分別執(zhí)行會(huì)打印A,B,C,如何編碼使得三個(gè)線程順序執(zhí)行,即輸出‘ABCABC’,這道題看似簡(jiǎn)單,但是如果對(duì)多線程不熟悉或者沒(méi)有準(zhǔn)備還真的不好回答。
下面列舉幾種比較好理解的方法,代碼手工編寫,都已測(cè)試
lock和Synchronized都是通過(guò)線程間的通信,喚醒指定的線程實(shí)現(xiàn)順序執(zhí)行
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只是為了控制打印的次數(shù)* @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();}}//否則就執(zhí)行任務(wù)并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循環(huán)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();}還有下面這種寫法,其實(shí)就是鎖對(duì)象換成了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,暫時(shí)還沒(méi)想到如何輸出多次
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方法會(huì)使得調(diào)用線程等待直到thread1結(jié)束diethread1.start();thread1.join();thread2.start();thread2.join();thread3.start();} }join的作用是當(dāng)前線程等待這個(gè)線程結(jié)束,底層實(shí)現(xiàn)就是循環(huán)判斷isAlive(),不斷的await。isAlive()是調(diào)用的系統(tǒng)本地方法
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 {//利用并發(fā)包里的Excutors的newSingleThreadExecutor產(chǎn)生一個(gè)單線程的線程池,// 而這個(gè)線程池的底層原理就是設(shè)置核心線程和最大線程都是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);}//作用是關(guān)閉線程池,終止線程執(zhí)行executorService.shutdown();} }SingleThreadExecutor底層其實(shí)就是創(chuàng)建一個(gè)核心線程池為1,無(wú)界隊(duì)列,最大線程為1的線程池
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}5.使用信號(hào)量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();}//這里判斷一次就可以控制執(zhí)行的次數(shù) System.out.println("a");count++;semaphore2.release();}});Thread thread2 = new Thread(()->{while (count < 4){try {//acquire方法會(huì)一直阻塞直到另一個(gè)線程釋放他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運(yùn)行。也就是必須有線程1先執(zhí)行,并釋放2的資源semaphore2.acquire();semaphore3.acquire();} catch (InterruptedException e) {e.printStackTrace();}thread2.start();thread1.start();thread3.start();} }總結(jié)
以上是生活随笔為你收集整理的面试必问,如何控制多个线程的执行顺序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: JVM调优技巧与经验
- 下一篇: 并发工具概览