日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
多線程交替輸出A1B2C3D4...你怎么實現(xiàn)?

引言

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

創(chuàng)新互聯(lián)建站是一家專業(yè)提供延吉企業(yè)網(wǎng)站建設,專注與網(wǎng)站設計制作、成都網(wǎng)站建設、html5、小程序制作等業(yè)務。10年已為延吉眾多企業(yè)、政府機構等服務。創(chuàng)新互聯(lián)專業(yè)的建站公司優(yōu)惠進行中。

面試問題是一個筆試題:

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

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

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

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

1. 使用線程方法

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

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

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

結合一個出讓等待的機制,就這樣交替實現(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();
    }
}

運行結果:

圖片

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

2. 使用CountDownLatch鐵門閂

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

具體來說,CountDownLatch有兩個主要方法:

  1. await()方法:調(diào)用該方法的線程會進入等待狀態(tài),直到計數(shù)器的值為0或者被中斷;
  2. countDown()方法:調(diào)用該方法會將計數(shù)器減1,當計數(shù)器的值為0時,會喚醒所有等待的線程。
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();
    }
}

運行結果:

圖片

3. 使用ReentrantLock

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

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

  1. await()方法:當前線程進入等待狀態(tài),并釋放鎖,直到其他線程使用signal()或signalAll()方法喚醒它;
  2. signal()方法:喚醒一個等待在該條件上的線程;
  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();
    }
}

運行結果:

圖片

Condition本質(zhì)是鎖資源上不同的等待隊列,我們也可以獲取不同的等待隊列來實現(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阻塞隊列

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

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

圖片

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

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

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

那么我們就利用這一點它必須要另外一個線程來取進而實現(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 queue = new LinkedTransferQueue();
        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();
    }
}

運行結果:

圖片

5. 使用LockSupport

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

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

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

當一個線程調(diào)用park()方法時,它會被阻塞,直到另一個線程調(diào)用該線程的unpark()方法才會被喚醒。

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

import java.util.concurrent.locks.LockSupport;

//Locksupport park 當前線程阻塞(停止)
//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();
    }
}

運行結果:

圖片

6. 使用枚舉類作同步標志

創(chuàng)建一個枚舉類ReadyToRun,利用while(true)死等和枚舉類指向?qū)ο蟛煌鳂酥疚唤惶孑敵觥?/p>

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();
    }
}

運行結果:

圖片

總結

好了,關于這個面試題的解法了不起暫時就想到這6種情況。

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

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


新聞名稱:多線程交替輸出A1B2C3D4...你怎么實現(xiàn)?
分享地址:http://www.dlmjj.cn/article/dppgcho.html