欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 用兩個(gè)線程交替打印數(shù)字和字母

 更新時(shí)間:2021年03月31日 09:37:27   作者:Bridge Li  
這篇文章主要介紹了Java 用兩個(gè)線程交替打印數(shù)字和字母的方法,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下

前一段時(shí)間聽(tīng)馬士兵老師講課,講到某公司的一個(gè)面試,兩個(gè)線程,其中一個(gè)線程輸出ABC,另一個(gè)線程輸出123,如何控制兩個(gè)線程交叉輸出1A2B3C,由于本人多線程掌握的一直不是很好,所以聽(tīng)完這道題,個(gè)人感覺(jué)收獲良多,這是一個(gè)學(xué)習(xí)筆記。這道題有多種解法,不過(guò)有些屬于純炫技,所以只記錄常見(jiàn)的三種解法。首先看第一種

1. park 和 unpark

package cn.bridgeli.demo;
 
import com.google.common.collect.Lists;
 
import java.util.List;
import java.util.concurrent.locks.LockSupport;
 
/**
 * @author BridgeLi
 * @date 2021/2/6 16:14
 */
public class Thread_Communication_Park_Unpark {
 
    static Thread t1 = null;
    static Thread t2 = null;
 
    public static void main(String[] args) {
 
        final List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        final List<String> strings = Lists.newArrayList("A", "B", "C", "D", "E", "F", "G");
 
        t1 = new Thread(() -> integers.forEach(item -> {
            System.out.print(item);
            LockSupport.unpark(t2);
            LockSupport.park();
        }), "t1");
 
        t2 = new Thread(() -> strings.forEach(item -> {
            LockSupport.park();
            System.out.print(item);
            LockSupport.unpark(t1);
        }), "t2");
 
        t1.start();
        t2.start();
    }
 
}

這個(gè)是最簡(jiǎn)單的實(shí)現(xiàn)方法,LockSupport.park() 使當(dāng)前線程阻塞,而 LockSupport.unpark() 則表示喚醒一個(gè)線程,所以他需要一個(gè)參數(shù),表示你要喚醒哪個(gè)線程,很好理解,也比較簡(jiǎn)單。

2. synchronized、notify、wait

package cn.bridgeli.demo;
 
import com.google.common.collect.Lists;
 
import java.util.List;
 
/**
 * @author BridgeLi
 * @date 2021/2/6 16:14
 */
public class Thread_Communication_Notify_Wait {
 
    public static void main(String[] args) {
 
        final Object o = new Object();
        final List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        final List<String> strings = Lists.newArrayList("A", "B", "C", "D", "E", "F", "G");
 
        new Thread(() -> {
            synchronized (o) {
                integers.forEach(item -> {
                    System.out.print(item);
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
 
                o.notify();
            }
        }, "t1").start();
 
        new Thread(() -> {
            synchronized (o) {
                strings.forEach(item -> {
                    System.out.print(item);
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                o.notify();
            }
        }, "t2").start();
    }
}

這是一個(gè)比較傳統(tǒng)的寫(xiě)法,也是比較難理解的一個(gè)寫(xiě)法,掌握了這種寫(xiě)法之后,對(duì) synchronized、notify、wait 的認(rèn)識(shí)也會(huì)有一個(gè)新高度,下面就簡(jiǎn)單解析一下這種寫(xiě)法:

我們都知道 synchronized 是一把鎖,而鎖是什么?就是一個(gè)第三方的互斥的一個(gè)資源,所以 synchronized (o),就表示我們對(duì) o 這個(gè)對(duì)象加鎖,是通過(guò)修改 o 的對(duì)象頭實(shí)現(xiàn)的,也就是兩個(gè)線程誰(shuí)成功修改了 o 的對(duì)象頭,那么誰(shuí)就拿到了這把鎖,然后就可以執(zhí)行里面的相關(guān)邏輯,而沒(méi)有成功修改 o 的對(duì)象頭的線程,就只有進(jìn)入到對(duì)象 o 的一個(gè)等待隊(duì)列,等待被系統(tǒng)調(diào)度執(zhí)行(這是一個(gè)比較簡(jiǎn)單的不是很準(zhǔn)確說(shuō)法,詳細(xì)過(guò)程,等我將來(lái)再寫(xiě)一個(gè)文章想聊鎖升級(jí)的過(guò)程);然后就是 o.notify(),剛說(shuō)過(guò) synchronized (o) 一堆線程爭(zhēng)搶鎖,沒(méi)有搶到鎖的線程進(jìn)入對(duì)象 o 的等待隊(duì)列,所以 o.notify() 含義就是從對(duì)象 o 的等待隊(duì)列中隨機(jī)叫醒一個(gè)線程,然后執(zhí)行;最后是 o.wait() 的含義,他的含義也很簡(jiǎn)單,就是當(dāng)前線程放到對(duì)象 o 的等待隊(duì)列中,讓出 CPU。

通過(guò)這段描述,所以大家肯定也可以學(xué)習(xí)到經(jīng)常遇到的三個(gè)問(wèn)題是怎么回事:1. wait 是否占用 CPU 資源,因?yàn)檫M(jìn)入了等待隊(duì)列,所以是不會(huì)占用的;2. 既然 notify、wait 是讓喚醒線程和讓線程進(jìn)入等待的,為什么不是 Thread 類的方法,反而是 Object 的方法,因?yàn)?notify、wait 是配合 synchronized 一起使用的,不一定用在多線程中,他們控制的是 synchronized 鎖定的對(duì)象的等待隊(duì)列,而 synchronized 鎖定的對(duì)象,肯定是一個(gè) Object,所以 notify、wait 比如是 Object 對(duì)象的方法;3. 關(guān)于 synchronized (o) 括號(hào)里面是一個(gè)對(duì)象實(shí)例、Class 對(duì)象、鎖定代碼塊、靜態(tài)變量等等區(qū)別,只要明白 synchronized 修改的是什么,這些區(qū)別就一目了然了,不再贅述。

最后要說(shuō)明的一個(gè)問(wèn)題是:循環(huán)外邊的 o.notify() 必不可少,有些同學(xué)寫(xiě)的時(shí)候可能隨手就忘記了,或者不知道為什么需要最后再 notify 一下,其實(shí)仔細(xì)想一想就可以明白了,假設(shè)最后執(zhí)行的是輸出字母的線程,那么他之前一定是被執(zhí)行輸出數(shù)字的線程喚醒的,而執(zhí)行輸出數(shù)字的這個(gè)線程喚醒執(zhí)行輸出字母的線程之后,自身就進(jìn)入等待隊(duì)列了,所以循環(huán)結(jié)束之后,如果最后執(zhí)行輸出字母的線程沒(méi)有喚醒執(zhí)行輸出數(shù)字的線程的話,那么執(zhí)行輸出數(shù)字的線程會(huì)一直 wait 阻塞在那里,將等到天荒地來(lái)??菔癄€永遠(yuǎn)無(wú)法結(jié)束。

3. Condition

package cn.bridgeli.demo;
 
import com.google.common.collect.Lists;
 
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * @author BridgeLi
 * @date 2021/2/6 16:14
 */
public class Thread_Communication_Condition {
 
    public static void main(String[] args) {
 
        final List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        final List<String> strings = Lists.newArrayList("A", "B", "C", "D", "E", "F", "G");
 
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
 
        new Thread(() -> {
            lock.lock();
            try {
                integers.forEach(item -> {
                    System.out.print(item);
                    condition2.signal();
                    try {
                        condition1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
 
                condition2.signal();
            } finally {
                lock.unlock();
            }
        }, "t1").start();
 
        new Thread(() -> {
            lock.lock();
            try {
                strings.forEach(item -> {
                    System.out.print(item);
                    condition1.signal();
                    try {
                        condition2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                condition1.signal();
            } finally {
                lock.unlock();
            }
        }, "t2").start();
 
    }
 
}

當(dāng)我們理解了上面兩種寫(xiě)法之后,那么最后這個(gè)寫(xiě)法其實(shí)也比較容易理解了,就不用我多贅言了。

如果有幸有同學(xué)看到了這里,那么我再提出一個(gè)小問(wèn)題,可以思考一下怎么解決,后面兩種寫(xiě)法,我們保證是執(zhí)行輸出數(shù)字的線程還是執(zhí)行輸出字母的線程先執(zhí)行,也就是先輸出數(shù)字或者字母嗎?如果不能的話,現(xiàn)在業(yè)務(wù)需求要求必須是先輸出字母或者數(shù)字怎么做?(提示:CAS 自旋)

以上就是Java 用兩個(gè)線程交替打印數(shù)字和字母的詳細(xì)內(nèi)容,更多關(guān)于Java 線程交替打印的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java基礎(chǔ)之StringBuffer詳解

    Java基礎(chǔ)之StringBuffer詳解

    這篇文章主要介紹了Java基礎(chǔ)之StringBuffer詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • java利用url實(shí)現(xiàn)網(wǎng)頁(yè)內(nèi)容的抓取

    java利用url實(shí)現(xiàn)網(wǎng)頁(yè)內(nèi)容的抓取

    本文主要介紹了java利用url實(shí)現(xiàn)網(wǎng)頁(yè)內(nèi)容抓取的示例。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-03-03
  • MyBatis源碼分析之日志記錄詳解

    MyBatis源碼分析之日志記錄詳解

    這篇文章主要給大家介紹了關(guān)于MyBatis源碼分析之日志記錄的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用MyBatis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • 詳細(xì)介紹使用Java調(diào)用Python的四種方法

    詳細(xì)介紹使用Java調(diào)用Python的四種方法

    這篇文章主要給大家介紹了關(guān)于使用Java調(diào)用Python的四種方法,每種方法根據(jù)實(shí)際項(xiàng)目需求有其適用場(chǎng)景,其中,推薦使用Runtime.getRuntime()方法,因?yàn)樗鼮楹?jiǎn)潔且易于實(shí)現(xiàn),需要的朋友可以參考下
    2024-10-10
  • 詳解SpringBoot統(tǒng)一響應(yīng)體解決方案

    詳解SpringBoot統(tǒng)一響應(yīng)體解決方案

    這篇文章主要介紹了詳解SpringBoot統(tǒng)一響應(yīng)體解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性

    詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性

    這篇文章主要介紹了詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • springboot整合nacos,如何讀取nacos配置文件

    springboot整合nacos,如何讀取nacos配置文件

    這篇文章主要介紹了springboot整合nacos,如何讀取nacos配置文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java控制臺(tái)輸入scanner的方法及代碼詳解

    Java控制臺(tái)輸入scanner的方法及代碼詳解

    在本篇內(nèi)容里小編給大家分享的是一篇關(guān)于Java控制臺(tái)輸入scanner的方法及實(shí)例代碼,有需要的朋友們可以參考下。
    2022-11-11
  • Mybatis 多對(duì)一查詢的實(shí)現(xiàn)方法

    Mybatis 多對(duì)一查詢的實(shí)現(xiàn)方法

    這篇文章主要介紹了Mybatis 多對(duì)一查詢,本文通過(guò)場(chǎng)景分析示例代碼相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • 一篇文章教會(huì)你使用java爬取想要的資源

    一篇文章教會(huì)你使用java爬取想要的資源

    這篇文章主要介紹了使用java爬蟲(chóng)爬取想要的資源,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08

最新評(píng)論