自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

多線程交替輸出A1B2C3D4...你怎么實(shí)現(xiàn)?

開發(fā) 前端
CountDownLatch是Java多線程中的一個(gè)同步工具類,它可以讓一個(gè)或多個(gè)線程等待其他線程完成操作后再繼續(xù)執(zhí)行。

引言

不知道最近有沒有小伙伴去面試,今天了不起回想到了早期去面試遇到的一個(gè)多線程面試問題。

面試問題是一個(gè)筆試題:

兩個(gè)線程依次交替輸出A~Z,1到26,形如A1B2C3D4...

當(dāng)時(shí)的我還很菜,用了原生的線程,借助wait和notify方法實(shí)現(xiàn)。

伙伴們你們也可以先暫停,自己思考下用什么方式來實(shí)現(xiàn)。

今天了不起和伙伴們一起來基于JDK1.8進(jìn)行實(shí)現(xiàn)方式的探索,請看下文。

1. 使用線程方法

wait()方法會(huì)使當(dāng)前線程釋放鎖,并進(jìn)入等待狀態(tài),直到以下情況之一發(fā)生:

  • 被其他線程調(diào)用notify()方法喚醒;
  • 被其他線程調(diào)用notifyAll()方法喚醒;
  • 被其他線程中斷。

notify()方法用于喚醒一個(gè)正在等待的線程,使其從wait()方法中返回。

結(jié)合一個(gè)出讓等待的機(jī)制,就這樣交替實(shí)現(xiàn)。

public class T06_00_sync_wait_notify {
    public static void main(String[] args) {
        final Object o = new Object();

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(()->{
            synchronized (o) {
                for(char c : aI) {
                    System.out.print(c);
                    try {
                        o.notify();
                        o.wait(); //讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                o.notify(); //必須,否則無法停止程序
            }

        }, "t1").start();

        new Thread(()->{
            synchronized (o) {
                for(char c : aC) {
                    System.out.print(c);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                o.notify();
            }
        }, "t2").start();
    }
}

運(yùn)行結(jié)果:

圖片圖片

思考:伙伴們,如果我想保證t2在t1之前打印,也就是說保證首先輸出的是A而不是1,這個(gè)時(shí)候該如何做?

2. 使用CountDownLatch鐵門閂

CountDownLatch是Java多線程中的一個(gè)同步工具類,它可以讓一個(gè)或多個(gè)線程等待其他線程完成操作后再繼續(xù)執(zhí)行。

具體來說,CountDownLatch有兩個(gè)主要方法:

  1. await()方法:調(diào)用該方法的線程會(huì)進(jìn)入等待狀態(tài),直到計(jì)數(shù)器的值為0或者被中斷;
  2. countDown()方法:調(diào)用該方法會(huì)將計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器的值為0時(shí),會(huì)喚醒所有等待的線程。
public class T07_00_sync_wait_notify {

    private static CountDownLatch latch = new CountDownLatch(1);

    public static void main(String[] args) {
        final Object o = new Object();



        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(()->{
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            synchronized (o) {
                for(char c : aI) {
                    System.out.print(c);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                o.notify();
            }
        }, "t1").start();

        new Thread(()->{

            synchronized (o) {
                for(char c : aC) {
                    System.out.print(c);
                    latch.countDown();
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        }, "t2").start();
    }
}

運(yùn)行結(jié)果:

圖片圖片

3. 使用ReentrantLock

我們可以通過ReentrantLock獲取條件鎖,通過它提供的方法來實(shí)現(xiàn)。

具體來說,ReentrantLock的Condition接口提供了以下三個(gè)方法:

  1. await()方法:當(dāng)前線程進(jìn)入等待狀態(tài),并釋放鎖,直到其他線程使用signal()或signalAll()方法喚醒它;
  2. signal()方法:喚醒一個(gè)等待在該條件上的線程;
  3. signalAll()方法:喚醒所有等待在該條件上的線程。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class T08_00_lock_condition {

    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aI) {
                    System.out.print(c);
                    condition.signal();
                    condition.await();
                }

                condition.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t1").start();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aC) {
                    System.out.print(c);
                    condition.signal();
                    condition.await();
                }

                condition.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t2").start();
    }
}

運(yùn)行結(jié)果:

圖片圖片

Condition本質(zhì)是鎖資源上不同的等待隊(duì)列,我們也可以獲取不同的等待隊(duì)列來實(shí)現(xiàn)。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class T09_00_lock_condition {

    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        Lock lock = new ReentrantLock();
        Condition conditionT1 = lock.newCondition();
        Condition conditionT2 = lock.newCondition();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aI) {
                    System.out.print(c);
                    conditionT2.signal();
                    conditionT1.await();
                }

                conditionT2.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t1").start();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aC) {
                    System.out.print(c);
                    conditionT1.signal();
                    conditionT2.await();
                }

                conditionT1.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t2").start();
    }
}

4. 使用TransferQueue阻塞隊(duì)列

TransferQueue是Java并發(fā)包中的一個(gè)阻塞隊(duì)列,它可以用于多線程之間的數(shù)據(jù)交換和同步。

LinkedTransferQueue繼承自TransferQueue,并且還可以支持異步操作。

圖片圖片

LinkedTransferQueue的take()方法和transfer()方法都是用于從隊(duì)列中取出元素的方法,但它們的使用場景和行為有所不同。

take()方法是一個(gè)阻塞方法,它會(huì)一直阻塞直到隊(duì)列中有可用元素,才將隊(duì)列中的元素取出并返回。

transfer()方法也是一個(gè)阻塞方法,它會(huì)將指定的元素插入到隊(duì)列中,并等待另一個(gè)線程從隊(duì)列中取出該元素。如果隊(duì)列中沒有等待的線程,則當(dāng)前線程會(huì)一直阻塞,直到有其他線程從隊(duì)列中取走該元素為止。

那么我們就利用這一點(diǎn)它必須要另外一個(gè)線程來取進(jìn)而實(shí)現(xiàn)把值交替輸出。

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;

public class T13_TransferQueue {
    public static void main(String[] args) {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        TransferQueue<Character> queue = new LinkedTransferQueue<Character>();
        new Thread(()->{
            try {
                for (char c : aI) {
                    System.out.print(queue.take());
                    queue.transfer(c);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(()->{
            try {
                for (char c : aC) {
                    queue.transfer(c);
                    System.out.print(queue.take());
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

運(yùn)行結(jié)果:

圖片圖片

5. 使用LockSupport

LockSupport是Java并發(fā)包中的一個(gè)工具類,它可以用于線程的阻塞和喚醒。

你可以把它類比成Object的wait()和notify()方法,但LockSupport是比它們更加靈活和可控的。

LockSupport提供了park()和unpark()方法:

當(dāng)一個(gè)線程調(diào)用park()方法時(shí),它會(huì)被阻塞,直到另一個(gè)線程調(diào)用該線程的unpark()方法才會(huì)被喚醒。

如果調(diào)用unpark()方法時(shí),該線程還沒有調(diào)用park()方法,則該線程調(diào)用park()方法時(shí)不會(huì)被阻塞,可以直接返回。

import java.util.concurrent.locks.LockSupport;

//Locksupport park 當(dāng)前線程阻塞(停止)
//unpark(Thread t)

public class T02_00_LockSupport {


    static Thread t1 = null, t2 = null;

    public static void main(String[] args) throws Exception {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        t1 = new Thread(() -> {

                for(char c : aI) {
                    System.out.print(c);
                    LockSupport.unpark(t2); //叫醒T2
                    LockSupport.park(); //T1阻塞
                }

        }, "t1");

        t2 = new Thread(() -> {

            for(char c : aC) {
                LockSupport.park(); //t2阻塞
                System.out.print(c);
                LockSupport.unpark(t1); //叫醒t1
            }

        }, "t2");

        t1.start();
        t2.start();
    }
}

運(yùn)行結(jié)果:

圖片圖片

6. 使用枚舉類作同步標(biāo)志

創(chuàng)建一個(gè)枚舉類ReadyToRun,利用while(true)死等和枚舉類指向?qū)ο蟛煌鳂?biāo)志位交替輸出。

public class T03_00_cas {

    enum ReadyToRun {T1, T2}

    static volatile ReadyToRun r = ReadyToRun.T1; 

    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(() -> {

            for (char c : aI) {
                while (r != ReadyToRun.T1) {}
                System.out.print(c);
                r = ReadyToRun.T2;
            }

        }, "t1").start();

        new Thread(() -> {

            for (char c : aC) {
                while (r != ReadyToRun.T2) {}
                System.out.print(c);
                r = ReadyToRun.T1;
            }
        }, "t2").start();
    }
}

運(yùn)行結(jié)果:

圖片圖片

總結(jié)

好了,關(guān)于這個(gè)面試題的解法了不起暫時(shí)就想到這6種情況。

這個(gè)面試題也是一道經(jīng)典的多線程面試題,如果你能將這幾種情況掌握,定會(huì)另面試官刮目相看。

如果你們還有新的方法歡迎和了不起一起探討研究,畢竟代碼是死的人是活的。

責(zé)任編輯:武曉燕 來源: Java面試教程
相關(guān)推薦

2024-05-10 07:44:23

C#進(jìn)程程序

2024-08-28 11:10:53

2024-11-15 11:00:00

C#多線程

2011-05-26 10:55:39

2024-12-03 00:44:50

2012-05-18 10:36:20

CC++編程

2009-08-12 18:04:44

編寫C#多線程

2021-03-05 07:38:52

C++線程編程開發(fā)技術(shù)

2023-05-03 09:01:41

CanvasWebGL

2015-03-24 13:46:29

C++多線程計(jì)數(shù)器特性實(shí)現(xiàn)

2009-08-26 14:35:00

用C#實(shí)現(xiàn)HTTP協(xié)議

2024-12-30 06:00:00

C#線程編程

2021-02-25 15:58:46

C++線程編程開發(fā)技術(shù)

2009-08-17 16:56:51

C#多線程控制進(jìn)度條

2011-09-07 10:00:53

Ubuntu3D

2010-01-18 14:09:58

C++多線程

2009-08-28 16:43:57

C#多線程學(xué)習(xí)

2010-02-04 10:19:39

C++多線程

2009-09-04 15:09:48

C#多線程啟動(dòng)Squa

2010-02-05 15:30:54

C++多線程測試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號