中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

Java、Kotlin、Go中線程與協(xié)程的區(qū)別-創(chuàng)新互聯(lián)

這篇文章給大家介紹Java、Kotlin、Go中線程與協(xié)程的區(qū)別,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

創(chuàng)新互聯(lián)建站從2013年開(kāi)始,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元化隆做網(wǎng)站,已為上家服務(wù),為化隆各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18980820575

協(xié)程是什么


協(xié)程并不是 Go 提出來(lái)的新概念,其他的一些編程語(yǔ)言,例如:Go、Python 等都可以在語(yǔ)言層面上實(shí)現(xiàn)協(xié)程,甚至是 Java,也可以通過(guò)使用擴(kuò)展庫(kù)來(lái)間接地支持協(xié)程。

當(dāng)在網(wǎng)上搜索協(xié)程時(shí),我們會(huì)看到:

  • Kotlin 官方文檔說(shuō)「本質(zhì)上,協(xié)程是輕量級(jí)的線程」。

  • 很多博客提到「不需要從用戶(hù)態(tài)切換到內(nèi)核態(tài)」、「是協(xié)作式的」等等。

「協(xié)程 Coroutines」源自 Simula 和 Modula-2 語(yǔ)言,這個(gè)術(shù)語(yǔ)早在 1958 年就被 Melvin Edward Conway 發(fā)明并用于構(gòu)建匯編程序,說(shuō)明協(xié)程是一種編程思想,并不局限于特定的語(yǔ)言。

協(xié)程的好處

性能比 Java 好很多,甚至代碼實(shí)現(xiàn)都比 Java 要簡(jiǎn)潔很多。

進(jìn)程

進(jìn)程是什么

計(jì)算機(jī)的核心是 CPU,執(zhí)行所有的計(jì)算任務(wù);操作系統(tǒng)負(fù)責(zé)任務(wù)的調(diào)度、資源的分配和管理;應(yīng)用程序是具有某種功能的程序,程序是運(yùn)行在操作系統(tǒng)上的。

進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序在一個(gè)數(shù)據(jù)集上的一次動(dòng)態(tài)執(zhí)行的過(guò)程,是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,是應(yīng)用程序運(yùn)行的載體。

進(jìn)程組成

進(jìn)程由三部分組成:

  • 程序:描述進(jìn)程要完成的功能,是控制進(jìn)程執(zhí)行的指令集。

  • 數(shù)據(jù)集合:程序在執(zhí)行時(shí)所需要的數(shù)據(jù)和工作區(qū)。

  • 進(jìn)程控制塊:(Program Control Block,簡(jiǎn)稱(chēng)PCB),包含進(jìn)程的描述信息和控制信息,是進(jìn)程存在的標(biāo)志。

進(jìn)程特征

  • 動(dòng)態(tài)性:進(jìn)程是程序的一次執(zhí)行過(guò)程,是臨時(shí)的,有生命期的,是動(dòng)態(tài)產(chǎn)生,動(dòng)態(tài)消亡的。

  • 并發(fā)性:任何進(jìn)程都可以同其他進(jìn)程一起并發(fā)執(zhí)行。

  • 獨(dú)立性:進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。結(jié)構(gòu)性:進(jìn)程由程序、數(shù)據(jù)和進(jìn)程控制塊三部分組成。

線程

線程是什么

線程是程序執(zhí)行中一個(gè)單一的順序控制流程,是程序執(zhí)行流的最小單元,是處理器調(diào)度和分派的基本單位。一個(gè)進(jìn)程可以有一個(gè)或多個(gè)線程,各個(gè)線程之間共享程序的內(nèi)存空間(也就是所在進(jìn)程的內(nèi)存空間)。

線程組成線程ID、當(dāng)前指令指針(PC)寄存器堆棧

任務(wù)調(diào)度

大部分操作系統(tǒng)(如Windows、Linux)的任務(wù)調(diào)度是采用時(shí)間片輪轉(zhuǎn)的搶占式調(diào)度方式。

在一個(gè)進(jìn)程中,當(dāng)一個(gè)線程任務(wù)執(zhí)行幾毫秒后,會(huì)由操作系統(tǒng)的內(nèi)核(負(fù)責(zé)管理各個(gè)任務(wù))進(jìn)行調(diào)度,通過(guò)硬件的計(jì)數(shù)器中斷處理器,讓該線程強(qiáng)制暫停并將該線程的寄存器放入內(nèi)存中,通過(guò)查看線程列表決定接下來(lái)執(zhí)行哪一個(gè)線程,并從內(nèi)存中恢復(fù)該線程的寄存器,最后恢復(fù)該線程的執(zhí)行,從而去執(zhí)行下一個(gè)任務(wù)。

進(jìn)程與線程的區(qū)別

線程是程序執(zhí)行的最小單位,而進(jìn)程是操作系統(tǒng)分配資源的最小單位;一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成,線程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線;進(jìn)程之間相互獨(dú)立,但同一進(jìn)程下的各個(gè)線程之間共享程序的內(nèi)存空間(包括代碼段、數(shù)據(jù)集、堆等)及一些進(jìn)程級(jí)的資源(如打開(kāi)文件和信號(hào)),某進(jìn)程內(nèi)的線程在其它進(jìn)程不可見(jiàn);調(diào)度和切換:線程上下文切換進(jìn)程上下文切換得多。

Java、Kotlin、Go中線程與協(xié)程的區(qū)別

線程的實(shí)現(xiàn)模型

程序一般不會(huì)直接去使用內(nèi)核線程,而是去使用內(nèi)核線程的一種高級(jí)接口——輕量級(jí)進(jìn)程(Lightweight Process,LWP),輕量級(jí)進(jìn)程就是我們通常意義上所講的線程,也被叫做用戶(hù)線程。

一對(duì)一模型

一個(gè)用戶(hù)線程對(duì)應(yīng)一個(gè)內(nèi)核線程,如果是多核的 CPU,那么線程之間是真正的并發(fā)。

缺點(diǎn):

  • 內(nèi)核線程的數(shù)量有限,一對(duì)一模型使用的用戶(hù)線程數(shù)量有限制。

  • 內(nèi)核線程的調(diào)度,上下文切換的開(kāi)銷(xiāo)較大(雖然沒(méi)有進(jìn)程上下文切換的開(kāi)銷(xiāo)大),導(dǎo)致用戶(hù)線程的執(zhí)行效率下降。

多對(duì)一模型

多個(gè)用戶(hù)線程映射到一個(gè)內(nèi)核線程上,線程間的切換由用戶(hù)態(tài)的代碼來(lái)進(jìn)行。用戶(hù)線程的建立、同步、銷(xiāo)毀都是在用戶(hù)態(tài)中完成,不需要內(nèi)核的介入。因此多對(duì)一的上下文切換速度快很多,且用戶(hù)線程的數(shù)量幾乎沒(méi)有限制。

缺點(diǎn):

  • 若一個(gè)用戶(hù)線程阻塞,其他所有線程都無(wú)法執(zhí)行,此時(shí)內(nèi)核線程處于阻塞狀態(tài)。

  • 處理器數(shù)量的增加,不會(huì)對(duì)多對(duì)一模型的線程性能造成影響,因?yàn)樗械挠脩?hù)線程都映射到了一個(gè)處理器上。

多對(duì)多模型

結(jié)合了一對(duì)一模型多對(duì)一模型的優(yōu)點(diǎn),多個(gè)用戶(hù)線程映射到多個(gè)內(nèi)核線程上,由線程庫(kù)負(fù)責(zé)在可用的可調(diào)度實(shí)體上調(diào)度用戶(hù)線程。這樣線程間的上下文切換很快,因?yàn)樗苊饬讼到y(tǒng)調(diào)用。但是增加了系統(tǒng)的復(fù)雜性。

優(yōu)點(diǎn):

一個(gè)用戶(hù)線程的阻塞不會(huì)導(dǎo)致所有線程的阻塞,因?yàn)榇藭r(shí)還有別的內(nèi)核線程被調(diào)度來(lái)執(zhí)行;多對(duì)多模型對(duì)用戶(hù)線程的數(shù)量沒(méi)有限制;在多處理器的操作系統(tǒng)中,多對(duì)多模型的線程也能得到一定的性能提升,但提升的幅度不如一對(duì)一模型的高。

線程的“并發(fā)”

只有在線程的數(shù)量 < 處理器的數(shù)量時(shí),線程的并發(fā)才是真正的并發(fā),這時(shí)不同的線程運(yùn)行在不同的處理器上。但是當(dāng)線程的數(shù)量 > 處理器的數(shù)量時(shí),會(huì)出現(xiàn)一個(gè)處理器運(yùn)行多個(gè)線程的情況。

在單個(gè)處理器運(yùn)行多個(gè)線程時(shí),并發(fā)是一種模擬出來(lái)的狀態(tài)。操作系統(tǒng)采用時(shí)間片輪轉(zhuǎn)的方式輪流執(zhí)行每一個(gè)線程?,F(xiàn)在,幾乎所有的現(xiàn)代操作系統(tǒng)采用的都是時(shí)間片輪轉(zhuǎn)的搶占式調(diào)度方式。

協(xié)程

當(dāng)在網(wǎng)上搜索協(xié)程時(shí),我們會(huì)看到:

本質(zhì)上,協(xié)程是輕量級(jí)的線程。很多博客提到「不需要從用戶(hù)態(tài)切換到內(nèi)核態(tài)」、「是協(xié)作式的」。

協(xié)程也并不是 Go 提出來(lái)的,協(xié)程是一種編程思想,并不局限于特定的語(yǔ)言。Go、Python、Kotlin 都可以在語(yǔ)言層面上實(shí)現(xiàn)協(xié)程,Java 也可以通過(guò)擴(kuò)展庫(kù)的方式間接支持協(xié)程。

協(xié)程比線程更加輕量級(jí),可以由程序員自己管理的輕量級(jí)線程,對(duì)內(nèi)核不可見(jiàn)。

協(xié)程的目的

在傳統(tǒng)的 J2EE 系統(tǒng)中都是基于每個(gè)請(qǐng)求占用一個(gè)線程去完成完整的業(yè)務(wù)邏輯(包括事務(wù))。所以系統(tǒng)的吞吐能力取決于每個(gè)線程的操作耗時(shí)。如果遇到很耗時(shí)的 I/O 行為,則整個(gè)系統(tǒng)的吞吐立刻下降,因?yàn)檫@個(gè)時(shí)候線程一直處于阻塞狀態(tài),如果線程很多的時(shí)候,會(huì)存在很多線程處于空閑狀態(tài)(等待該線程執(zhí)行完才能執(zhí)行),造成了資源應(yīng)用不徹底。

最常見(jiàn)的例子就是 JDBC(它是同步阻塞的),這也是為什么很多人都說(shuō)數(shù)據(jù)庫(kù)是瓶頸的原因。這里的耗時(shí)其實(shí)是讓 CPU 一直在等待 I/O 返回,說(shuō)白了線程根本沒(méi)有利用 CPU 去做運(yùn)算,而是處于空轉(zhuǎn)狀態(tài)。而另外過(guò)多的線程,也會(huì)帶來(lái)更多的 ContextSwitch 開(kāi)銷(xiāo)。

對(duì)于上述問(wèn)題,現(xiàn)階段行業(yè)里的比較流行的解決方案之一就是單線程加上異步回調(diào)。其代表派是 node.js 以及 Java 里的新秀 Vert.x。

而協(xié)程的目的就是當(dāng)出現(xiàn)長(zhǎng)時(shí)間的 I/O 操作時(shí),通過(guò)讓出目前的協(xié)程調(diào)度,執(zhí)行下一個(gè)任務(wù)的方式,來(lái)消除 ContextSwitch 上的開(kāi)銷(xiāo)。

協(xié)程的特點(diǎn)線程的切換由操作系統(tǒng)負(fù)責(zé)調(diào)度,協(xié)程由用戶(hù)自己進(jìn)行調(diào)度,減少了上下文切換,提高了效率線程的默認(rèn) Stack 是1M,協(xié)程更加輕量,是 1K,在相同內(nèi)存中可以開(kāi)啟更多的協(xié)程。由于在同一個(gè)線程上,因此可以避免競(jìng)爭(zhēng)關(guān)系而使用鎖。適用于被阻塞的,且需要大量并發(fā)的場(chǎng)景。但不適用于大量計(jì)算的多線程,遇到此種情況,更好用線程去解決。

協(xié)程的原理

當(dāng)出現(xiàn)IO阻塞的時(shí)候,由協(xié)程的調(diào)度器進(jìn)行調(diào)度,通過(guò)將數(shù)據(jù)流立刻yield掉(主動(dòng)讓出),并且記錄當(dāng)前棧上的數(shù)據(jù),阻塞完后立刻再通過(guò)線程恢復(fù)棧,并把阻塞的結(jié)果放到這個(gè)線程上去跑,這樣看上去好像跟寫(xiě)同步代碼沒(méi)有任何差別,這整個(gè)流程可以稱(chēng)為coroutine,而跑在由coroutine負(fù)責(zé)調(diào)度的線程稱(chēng)為Fiber。比如Golang里的 go關(guān)鍵字其實(shí)就是負(fù)責(zé)開(kāi)啟一個(gè)Fiber,讓func邏輯跑在上面。

由于協(xié)程的暫停完全由程序控制,發(fā)生在用戶(hù)態(tài)上;而線程的阻塞狀態(tài)是由操作系統(tǒng)內(nèi)核來(lái)進(jìn)行切換,發(fā)生在內(nèi)核態(tài)上。
因此,協(xié)程的開(kāi)銷(xiāo)遠(yuǎn)遠(yuǎn)小于線程的開(kāi)銷(xiāo),也就沒(méi)有了 ContextSwitch 上的開(kāi)銷(xiāo)。

假設(shè)程序中默認(rèn)創(chuàng)建兩個(gè)線程為協(xié)程使用,在主線程中創(chuàng)建協(xié)程ABCD…,分別存儲(chǔ)在就緒隊(duì)列中,調(diào)度器首先會(huì)分配一個(gè)工作線程A執(zhí)行協(xié)程A,另外一個(gè)工作線程B執(zhí)行協(xié)程B,其它創(chuàng)建的協(xié)程將會(huì)放在隊(duì)列中進(jìn)行排隊(duì)等待。

Java、Kotlin、Go中線程與協(xié)程的區(qū)別

當(dāng)協(xié)程A調(diào)用暫停方法或被阻塞時(shí),協(xié)程A會(huì)進(jìn)入到掛起隊(duì)列,調(diào)度器會(huì)調(diào)用等待隊(duì)列中的其它協(xié)程搶占線程A執(zhí)行。當(dāng)協(xié)程A被喚醒時(shí),它需要重新進(jìn)入到就緒隊(duì)列中,通過(guò)調(diào)度器搶占線程,如果搶占成功,就繼續(xù)執(zhí)行協(xié)程A,失敗則繼續(xù)等待搶占線程。

Java、Kotlin、Go中線程與協(xié)程的區(qū)別

Java、Kotlin、Go 的線程與協(xié)程

Java 在 Linux 操作系統(tǒng)下使用的是用戶(hù)線程+輕量級(jí)線程,一個(gè)用戶(hù)線程映射到一個(gè)內(nèi)核線程,線程之間的切換就涉及到了上下文切換。所以在 Java 中并不適合創(chuàng)建大量的線程,否則效率會(huì)很低??梢韵瓤聪?Kotlin 和 Go 的協(xié)程:

Kotlin 的協(xié)程

Kotlin 在誕生之初,目標(biāo)就是完全兼容 Java,卻是一門(mén)非常務(wù)實(shí)的語(yǔ)言,其中一個(gè)特性,就是支持協(xié)程。

但是 Kotlin 最終還是運(yùn)行在 JVM 中的,目前的 JVM 并不支持協(xié)程,Kotlin 作為一門(mén)編程語(yǔ)言,也只是能在語(yǔ)言層面支持協(xié)程。Kotlin 的協(xié)程是用于異步編程等場(chǎng)景的,在語(yǔ)言級(jí)提供協(xié)程支持,而將大部分功能委托給庫(kù)。

使用「線程」的代碼

@Test
fun testThread() {
 // 執(zhí)行時(shí)間 1min+
 val c = AtomicLong()
 for (i in 1..1_000_000L)
 thread(start = true) {
  c.addAndGet(i)
 }
 println(c.get())
}

上述代碼創(chuàng)建了100 萬(wàn)個(gè)線程,在每個(gè)線程里僅僅調(diào)用了 add 操作,但是由于創(chuàng)建線程太多,這個(gè)測(cè)試用例在我的機(jī)器上要跑 1 分鐘左右。

使用「協(xié)程」的代碼

@Test
fun testLaunch() {
 val c = AtomicLong()
 runBlocking {
 for (i in 1..1_000_000L)
  launch {
  c.addAndGet(workload(i))
  }
 }
 print(c.get())
}

suspend fun workload(n: Long): Long {
 delay(1000)
 return n

這段代碼是創(chuàng)建了100 萬(wàn)個(gè)協(xié)程,測(cè)試用例在我的機(jī)器上執(zhí)行時(shí)間大概是 10 秒鐘。而且這段代碼的每個(gè)協(xié)程都 delay 了 1 秒鐘,執(zhí)行效率仍然遠(yuǎn)遠(yuǎn)高于線程。

詳細(xì)的語(yǔ)法可以查看 Kotlin 的官方網(wǎng)站:https://www.kotlincn.net/docs/reference/coroutines/basics.html

其中關(guān)鍵字launch 是開(kāi)啟了一個(gè)協(xié)程,關(guān)鍵字suspend 是掛起一個(gè)協(xié)程,而不會(huì)阻塞。現(xiàn)在在看這個(gè)流程,應(yīng)該就懂了~

Java、Kotlin、Go中線程與協(xié)程的區(qū)別

Go 的協(xié)程

官方例程:https://gobyexample-cn.github.io/goroutines

go語(yǔ)言層面并不支持多進(jìn)程或多線程,但是協(xié)程更好用,協(xié)程被稱(chēng)為用戶(hù)態(tài)線程,不存在CPU上下文切換問(wèn)題,效率非常高。下面是一個(gè)簡(jiǎn)單的協(xié)程演示代碼:

package main

func main() {
 go say("Hello World")
}

func say(s string) {
 println(s)
}

Java 的 Kilim 協(xié)程框架

目前 Java 原生語(yǔ)言暫時(shí)不支持協(xié)程,可以使用 kilim,具體原理可以看官方文檔,暫時(shí)還沒(méi)有研究~

Java 的 Project Loom

Java 也在逐步支持協(xié)程,其項(xiàng)目就是Project Loom(https://openjdk.java.net/projects/loom/)。這個(gè)項(xiàng)目在18年底的時(shí)候已經(jīng)達(dá)到可初步演示的原型階段。不同于之前的方案,Project Loom 是從 JVM 層面對(duì)多線程技術(shù)進(jìn)行徹底的改變。

官方介紹:
http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html

其中一段介紹了為什么引入這個(gè)項(xiàng)目:

One of Java's most important contributions when it was first released, over twenty years ago, was the easy access to threads and synchronization primitives. Java threads (either used directly, or indirectly through, for example, Java servlets processing HTTP requests) provided a relatively simple abstraction for writing concurrent applications. These days, however, one of the main difficulties in writing concurrent programs that meet today's requirements is that the software unit of concurrency offered by the runtime — the thread — cannot match the scale of the domain's unit of concurrency, be it a user, a transaction or even a single operation. Even if the unit of application concurrency is coarse — say, a session, represented by single socket connection — a server can handle upward of a million concurrent open sockets, yet the Java runtime, which uses the operating system's threads for its implementation of Java threads, cannot efficiently handle more than a few thousand. A mismatch in several orders of magnitude has a big impact.

文章大意就是本文上面所說(shuō)的,Java 的用戶(hù)線程與內(nèi)核線程是一對(duì)一的關(guān)系,一個(gè) Java 進(jìn)程很難創(chuàng)建上千個(gè)線程,如果是對(duì)于 I/O 阻塞的程序(例如數(shù)據(jù)庫(kù)讀取/Web服務(wù)),性能會(huì)很低下,所以要采用類(lèi)似于協(xié)程的機(jī)制。

使用 Fiber

在引入 Project Loom 之后,JDK 將引入一個(gè)新類(lèi):java.lang.Fiber。此類(lèi)與 java.lang.Thread 一起,都成為了 java.lang.Strand 的子類(lèi)。即線程變成了一個(gè)虛擬的概念,有兩種實(shí)現(xiàn)方法:Fiber 所表示的輕量線程和 Thread 所表示的傳統(tǒng)的重量級(jí)線程。

Fiber f = Fiber.schedule(() -> {
 println("Hello 1");
 lock.lock(); // 等待鎖不會(huì)掛起線程
 try {
 println("Hello 2");
 } finally {
 lock.unlock();
 }
 println("Hello 3");
})

只需執(zhí)行Fiber.schedule(Runnable task) 就能在Fiber 中執(zhí)行任務(wù)。最重要的是,上面例子中的 lock.lock() 操作將不再掛起底層線程。除了Lock 不再掛起線程以外,像Socket BIO 操作也不再掛起線程。 但 synchronized,以及 Native 方法中線程掛起操作無(wú)法避免。

關(guān)于Java、Kotlin、Go中線程與協(xié)程的區(qū)別就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

當(dāng)前文章:Java、Kotlin、Go中線程與協(xié)程的區(qū)別-創(chuàng)新互聯(lián)
鏈接URL:http://www.rwnh.cn/article34/hdhse.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶(hù)體驗(yàn)、網(wǎng)站設(shè)計(jì)公司、網(wǎng)站設(shè)計(jì)、軟件開(kāi)發(fā)、App開(kāi)發(fā)、做網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)
民县| 方正县| 辽中县| 景德镇市| 呈贡县| 波密县| 太湖县| 道真| 湟中县| 荆州市| 白银市| 乐安县| 武清区| 永登县| 易门县| 正蓝旗| 苍山县| 离岛区| 龙门县| 孝昌县| 响水县| 巴林右旗| 哈密市| 黄大仙区| 浦东新区| 岚皋县| 凤台县| 都兰县| 定西市| 定襄县| 泾源县| 左权县| 汉中市| 苍山县| 高安市| 江西省| 河源市| 鹤壁市| 乐亭县| 德阳市| 天镇县|