天天看點

Kotlin(3)-協程和操作符重載,Java進階工程師面試視訊2021年Java中進階面試必備知識點總結

協程和線程的聯系和差別

在作業系統OS中,程序是資源配置設定的最小機關,線程是任務排程的最小機關。而協程則是處線上程内部的**“微線程”,或者說輕量級線程**。 由于線程在OS中是稀缺資源,所有OS平台的線程數量都是有上限的,平時程式設計我們會用線程池來管理線程數量,熟悉線程池的同學應該知道,線程池管理線程,無論是核心線程還是非核心線程,都不會随意去建立,除非迫不得已。

用線程解決異步問題

  • 多線程同步程式設計可以通過加鎖解決資料的線程安全問題,但是加鎖會降低程式執行效率,并且鎖多了,會有死鎖隐患
  • 線程的狀态轉換完全由核心控制,程式員開發者無法幹涉
  • 線程的是稀缺資源,不能随意建立,使用線程解決異步問題,線程的初始化,上下文切換(CPU輪轉),線程狀态切換(sleep,yield…), 變量加鎖操作(synchronized關鍵字),都會使得線程的使用代價比較大

用協程解決異步問題

  • 協程是運作線上程之上的優化産物,或稱“微線程”。協程依賴線程運作,複雜的底層邏輯被封裝在庫内,使用時無需關心所處線程狀态
  • 使用協程,開發者可以自己控制協程的狀态(suspend挂起,resume恢複),而不會像線程那樣依賴底層排程,時間片争奪。
  • 一個線程可以跑多個協程,一個協程也可以分段在多個線程上執行
  • 協程 是 非阻塞的,目前協程挂起之後,所線上程資源并不會浪費,它會去執行其他協程(如果有的話)
  • 協程 相對于線程這種OS中的稀缺資源,它是極其輕量級的,就算你開一百萬個協程,對于系統的壓力也不會像大量線程那樣大(别說一百萬個,linux系統的線程數量上線是1000,超過這個值系統就無法正常運作).
  • 總之一句話 : 協程的出現,讓程式開發者對程式邏輯的掌控提升到了一個新的境界,想象一下,一個函數正在執行,你想讓他在某個時刻暫停,然後在另一個時刻繼續。利用線程恐怕很難做到。協程中,輕而易舉。

基本使用

module級别的build.gradle 中 引入庫依賴,推薦使用最新版(目前穩定版是1.3.3)

dependencies {
    //...
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3" // 如果你需要用到協程排程器Dispatchers的話,必須加上這個依賴
}
           
協程的建立

建立協程有多種方式,全局/獨立,同步/異步,并且可以指定 “協程排程器”

  • runBlocking
    fun main() {
        runBlocking {
            println("這是runBlocking協程...")
        }
    }
               
  • launch
    fun main() {
        runBlocking {
            println("這是runBlocking協程...")
            launch {
                println("這是runBlocking内部的runBlocking協程...")
                delay(2000)
                println("延遲了2000MS之後,再列印")
            }
        }
    }
               
  • GlobalScope.launch
    fun main() {
        println("協程,相對于主線程來說,都是異步的,也就是說,你在這裡插入協程邏輯,主線程的邏輯并不會被阻塞")
        GlobalScope.launch {
            delay(3000)
            println("GlobalScope.launch 建立協程 ")
        }
        println("主線程繼續")
        Thread.sleep(5000)
    }
               
  • Global.async
    fun main() {
        runBlocking {
            val async: Deferred<String> = GlobalScope.async {
                println("這是一個異步協程,他将傳回一個Deferred")
                delay(2000)
                "異步任務傳回值"
            }
            println("主線程繼續:" + async.await())
        }
    
        Thread.sleep(5000)
    }
               
“騷操作”

關心協程的人一般都會十分關注它到底能給我們異步程式設計帶來怎樣的便利。這裡總結幾個不用協程實作起來很麻煩的騷操作。

  • 如果有一個函數,它的傳回值需要等到多個耗時的異步任務都執行完畢傳回之後,組合所有任務的傳回值作為 最終傳回值
    fun test6(): String = runBlocking {
        var finalRes = ""
        coroutineScope {
            launch {
                delay(1000)
                finalRes = finalRes.plus("1")
            }
            launch {
                delay(2000)
                finalRes = finalRes.plus("2")
            }
    
            launch {
                delay(3000)
                finalRes = finalRes.plus("3")
            }
        }
        finalRes
    }
    
    fun main() {
        val test6 = test6()
        println("最終傳回值是: $test6")
    }
               
    最終傳回結果為(延遲3秒之後列印):
  • 如果有一個函數,需要順序執行多個網絡請求,并且後一個請求依賴前一個請求的執行結果
    import kotlinx.coroutines.*
    
    suspend fun getToken(): String {
        for (i in 0..10) {
            println("異步請求正在執行:getToken :$i")
            delay(100)
        }
        return "ask"
    }
    
    suspend fun getResponse(token: String): String {
        for (i in 0..10) {
            println("異步請求正在執行:getResponse :$token $i")
            delay(100)
        }
    
        return "response"
    }
    
    fun setText(response: String) {
        println("setText 執行,時間:  ${System.currentTimeMillis()}")
    }
    
    fun main() {
        GlobalScope.launch(Dispatchers.Unconfined) {
            var token = GlobalScope.async(Dispatchers.Unconfined) {
                return@async getToken()
            }.await() // 建立異步任務,并且 阻塞執行 await 是阻塞執行取得結果
    
            var response = GlobalScope.async(Dispatchers.Unconfined) {
                return@async getResponse(token)
            }.await() // 建立異步任務,并且立即執行
    
            setText(response)
        }
    
        Thread.sleep(20000)
    }
               
    執行結果:
    異步請求正在執行:getToken :0
    異步請求正在執行:getToken :1
    異步請求正在執行:getToken :2
    異步請求正在執行:getToken :3
    異步請求正在執行:getToken :4
    異步請求正在執行:getToken :5
    異步請求正在執行:getToken :6
    異步請求正在執行:getToken :7
    異步請求正在執行:getToken :8
    異步請求正在執行:getToken :9
    異步請求正在執行:getToken :10
    異步請求正在執行:getResponse :ask 0
    異步請求正在執行:getResponse :ask 1
    異步請求正在執行:getResponse :ask 2
    異步請求正在執行:getResponse :ask 3
    異步請求正在執行:getResponse :ask 4
    異步請求正在執行:getResponse :ask 5
    異步請求正在執行:getResponse :ask 6
    異步請求正在執行:getResponse :ask 7
    異步請求正在執行:getResponse :ask 8
    異步請求正在執行:getResponse :ask 9
    異步請求正在執行:getResponse :ask 10
    setText 執行,時間:  1578904290520
               
  • 目前正在執行一項異步任務,但是你突然不想要它執行了,随時可以取消
    fun main() {
        // 協程任務
        val job = GlobalScope.launch(Dispatchers.IO) {
            for (i in 0..100){// 每次挂起100MS,100次也就是10秒
                println("協程正在執行 $i")
                delay(100)
            }
        }
    
        // 但是我在1秒之後就取消協程
        Thread.sleep(1000)
        job?.cancel()
        println( "btn_right 結束協程")
    }
               
    執行結果(本該執行100輪的列印,隻持續了10輪):
    協程正在執行 0
    協程正在執行 1
    協程正在執行 2
    協程正在執行 3
    協程正在執行 4
    協程正在執行 5
    協程正在執行 6
    協程正在執行 7
    協程正在執行 8
    協程正在執行 9
    btn_right 結束協程
    
    Process finished with exit code 0
               
  • 如果你想讓一個任務最多執行3秒,超過3秒則自動取消
    import kotlinx.coroutines.*
    
    
    fun main() = runBlocking {
        println("限時任務中結果是:" + getResFromTimeoutTask())
    }
    
    suspend fun getResFromTimeoutTask(): String? {
        // 忘了,它會保證内部的協程代碼都執行完畢,是以不能這麼寫
        return withTimeoutOrNull(1300) {
            for (i in 0..10) {
                println("I'm sleeping $i ...")
                delay(500)
            }
            "執行結束"
        }
    }
               
    執行結果
    I'm sleeping 0 ...
    I'm sleeping 1 ...
    I'm sleeping 2 ...
    限時任務中結果是:null
    
    Process finished with exit code 0
               

總結

協程作為kotlin 差別于java的新概念,它的出現是為了解決java不好解決的問題,比如層層回調導緻代碼臃腫,比如 異步任務執行流程不好操控等。本章節篇幅有限,無法展開說明,但是對于新手而言,看完本章應該能對協程的作用有一個大概的認知。本人也是初步研究,後續有更深入的了解之後,再進行專文講解吧。

操作符重載

概念

說人話,像是一進制操作符 ++自加,二進制操作符 +相加 ,預設隻支援數字類型,比如Int. 但是通過操作符的重載,我們可以讓任意類 都能 ++自加,且傳回一個想要的對象。操作符執行的邏輯,完全看我們如何去設計。

分類

按元素級别

  • 一進制
    表達式 對應函數
    +a a.unaryPlus()
    -a a.unaryMinus()
    !a a.not()
    a++ a.inc()
    a– a.dec()
  • 二進制
    表達式 對應函數
    a+b a.plus(b)
    a-b a.minus(b)
    a*b a.times(b)
    a/b a.div(b)
    a%b a.rem(b)
    a…b a.range(b)
    a in b b.contains(a)
    a !in b !b.contains(a)
    a[i] a.get(i)
    a[i,j] a.get(i,j)
    a[i_1,…,i_n] a.get(i_1,…,i_n)
    a[i]=b a.set(i,b)
    a[i,j]=b a.set(i,j,b)
    a[i_1,…,i_n]=b a.set(i_1,…,i_j,b)
    a() a.invoke()
    a(i) a.invoke(i)
    a(i,j) a.invoke(i,j)
    a(i_1,…,i_n) a.invoke(i_1,…,i_n)
    a+=b a.plusAssign(b)
    a-=b a.minusAssign(b)
    a*=b a.timesAssign(b)
    a/=b a.divAssign(b)
    a%=b a.modAssign(b)
    a > b a.compareTo(b)>0
    a < b a.compareTo(b)<0
    a>=b a.compareTo(b)>=0
    a<=b a.compareTo(b)<=0

按實作方式

  • 成員函數
  • 擴充函數

栗子

2021年Java中進階面試必備知識點總結

在這個部分總結了2019年到目前為止Java常見面試問題,取其面試核心編寫成這份文檔筆記,從中分析面試官的心理,摸清面試官的“套路”,可以說搞定90%以上的Java中進階面試沒一點難度。

本節總結的内容涵蓋了:消息隊列、Redis緩存、分庫分表、讀寫分離、設計高并發系統、分布式系統、高可用系統、SpringCloud微服務架構等一系列網際網路主流進階技術的知識點。

目錄:

Kotlin(3)-協程和操作符重載,Java進階工程師面試視訊2021年Java中進階面試必備知識點總結

(上述隻是一個整體目錄大綱,每個點裡面都有如下所示的詳細内容,從面試問題——分析面試官心理——剖析面試題——完美解答的一個過程)

Kotlin(3)-協程和操作符重載,Java進階工程師面試視訊2021年Java中進階面試必備知識點總結

部分内容:

Kotlin(3)-協程和操作符重載,Java進階工程師面試視訊2021年Java中進階面試必備知識點總結
Kotlin(3)-協程和操作符重載,Java進階工程師面試視訊2021年Java中進階面試必備知識點總結
Kotlin(3)-協程和操作符重載,Java進階工程師面試視訊2021年Java中進階面試必備知識點總結

對于每一個做技術的來說,學習是不能停止的,小編把2019年到目前為止Java的核心知識提煉出來了,無論你現在是處于什麼階段,如你所見,這份文檔的内容無論是對于你找面試工作還是提升技術廣度深度都是完美的。

不想被後浪淘汰的話,趕緊搞起來吧,高清完整版一共是888頁,需要的話可以點贊+關注

CodeChina開源項目:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視訊】

COb-1630560593847)]

[外鍊圖檔轉存中…(img-UMReCOuZ-1630560593848)]

[外鍊圖檔轉存中…(img-vFT1v3aV-1630560593850)]

對于每一個做技術的來說,學習是不能停止的,小編把2019年到目前為止Java的核心知識提煉出來了,無論你現在是處于什麼階段,如你所見,這份文檔的内容無論是對于你找面試工作還是提升技術廣度深度都是完美的。

不想被後浪淘汰的話,趕緊搞起來吧,高清完整版一共是888頁,需要的話可以點贊+關注

CodeChina開源項目:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視訊】