日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发编程实战~协程

發布時間:2024/7/23 java 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发编程实战~协程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Golang 是一門號稱從語言層面支持并發的編程語言,支持并發是 Golang 一個非常重要的特性。在上一篇文章《44 | 協程:更輕量級的線程》中我們介紹過,Golang 支持協程,協程可以類比 Java 中的線程,解決并發問題的難點就在于線程(協程)之間的協作。

那 Golang 是如何解決協作問題的呢?

總的來說,Golang 提供了兩種不同的方案:一種方案支持協程之間以共享內存的方式通信,Golang 提供了管程和原子類來對協程進行同步控制,這個方案與 Java 語言類似;另一種方案支持協程之間以消息傳遞(Message-Passing)的方式通信,本質上是要避免共享,Golang 的這個方案是基于 CSP(Communicating Sequential Processes)模型實現的。Golang 比較推薦的方案是后者。

什么是 CSP 模型

我們在《42 | Actor 模型:面向對象原生的并發模型》中介紹了 Actor 模型,Actor 模型中 Actor 之間就是不能共享內存的,彼此之間通信只能依靠消息傳遞的方式。Golang 實現的 CSP 模型和 Actor 模型看上去非常相似,Golang 程序員中有句格言:“不要以共享內存方式通信,要以通信方式共享內存(Don’t communicate by sharing memory, share memory by communicating)。”雖然 Golang 中協程之間,也能夠以共享內存的方式通信,但是并不推薦;而推薦的以通信的方式共享內存,實際上指的就是協程之間以消息傳遞方式來通信。

下面我們先結合一個簡單的示例,看看 Golang 中協程之間是如何以消息傳遞的方式實現通信的。我們示例的目標是打印從 1 累加到 100 億的結果,如果使用單個協程來計算,大概需要 4 秒多的時間。單個協程,只能用到 CPU 中的一個核,為了提高計算性能,我們可以用多個協程來并行計算,這樣就能發揮多核的優勢了。

在下面的示例代碼中,我們用了 4 個子協程來并行執行,這 4 個子協程分別計算[1, 25 億]、(25 億, 50 億]、(50 億, 75 億]、(75 億, 100 億],最后再在主協程中匯總 4 個子協程的計算結果。主協程要匯總 4 個子協程的計算結果,勢必要和 4 個子協程之間通信,Golang 中協程之間通信推薦的是使用 channel,channel 你可以形象地理解為現實世界里的管道。另外,calc() 方法的返回值是一個只能接收數據的 channel ch,它創建的子協程會把計算結果發送到這個 ch 中,而主協程也會將這個計算結果通過 ch 讀取出來。

import ("fmt""time" ) func main() {// 變量聲明var result, i uint64// 單個協程執行累加操作start := time.Now()for i = 1; i <= 10000000000; i++ {result += i}// 統計計算耗時elapsed := time.Since(start)fmt.Printf("執行消耗的時間為:", elapsed)fmt.Println(", result:", result)// 4個協程共同執行累加操作start = time.Now()ch1 := calc(1, 2500000000)ch2 := calc(2500000001, 5000000000)ch3 := calc(5000000001, 7500000000)ch4 := calc(7500000001, 10000000000)// 匯總4個協程的累加結果result = <-ch1 + <-ch2 + <-ch3 + <-ch4// 統計計算耗時elapsed = time.Since(start)fmt.Printf("執行消耗的時間為:", elapsed)fmt.Println(", result:", result) } // 在協程中異步執行累加操作,累加結果通過channel傳遞 func calc(from uint64, to uint64) <-chan uint64 {// channel用于協程間的通信ch := make(chan uint64)// 在協程中執行累加操作go func() {result := fromfor i := from + 1; i <= to; i++ {result += i}// 將結果寫入channelch <- result}()// 返回結果是用于通信的channelreturn ch }

CSP 模型與生產者 - 消費者模式

你可以簡單地把 Golang 實現的 CSP 模型類比為生產者 - 消費者模式,而 channel 可以類比為生產者 - 消費者模式中的阻塞隊列。不過,需要注意的是 Golang 中 channel 的容量可以是 0,容量為 0 的 channel 在 Golang 中被稱為無緩沖的 channel,容量大于 0 的則被稱為有緩沖的 channel。

無緩沖的 channel 類似于 Java 中提供的 SynchronousQueue,主要用途是在兩個協程之間做數據交換。比如上面累加器的示例代碼中,calc() 方法內部創建的 channel 就是無緩沖的 channel。
而創建一個有緩沖的 channel 也很簡單,在下面的示例代碼中,我們創建了一個容量為 4 的 channel,同時創建了 4 個協程作為生產者、4 個協程作為消費者。

// 創建一個容量為4的channel ch := make(chan int, 4) // 創建4個協程,作為生產者 for i := 0; i < 4; i++ {go func() {ch <- 7}() } // 創建4個協程,作為消費者 for i := 0; i < 4; i++ {go func() {o := <-chfmt.Println("received:", o)}() }

Golang 中的 channel 是語言層面支持的,所以可以使用一個左向箭頭(<-)來完成向 channel 發送數據和讀取數據的任務,使用上還是比較簡單的。Golang 中的 channel 是支持雙向傳輸的,所謂雙向傳輸,指的是一個協程既可以通過它發送數據,也可以通過它接收數據。

不僅如此,Golang 中還可以將一個雙向的 channel 變成一個單向的 channel,在累加器的例子中,calc() 方法中創建了一個雙向 channel,但是返回的就是一個只能接收數據的單向 channel,所以主協程中只能通過它接收數據,而不能通過它發送數據,如果試圖通過它發送數據,編譯器會提示錯誤。對比之下,雙向變單向的功能,如果以 SDK 方式實現,還是很困難的。

CSP 模型與 Actor 模型的區別

同樣是以消息傳遞的方式來避免共享,那 Golang 實現的 CSP 模型和 Actor 模型有什么區別呢?

第一個最明顯的區別就是:Actor 模型中沒有 channel。雖然 Actor 模型中的 mailbox 和 channel 非常像,看上去都像個 FIFO 隊列,但是區別還是很大的。Actor 模型中的 mailbox 對于程序員來說是“透明”的,mailbox 明確歸屬于一個特定的 Actor,是 Actor 模型中的內部機制;而且 Actor 之間是可以直接通信的,不需要通信中介。但 CSP 模型中的 channel 就不一樣了,它對于程序員來說是“可見”的,是通信的中介,傳遞的消息都是直接發送到 channel 中的。

第二個區別是:Actor 模型中發送消息是非阻塞的,而 CSP 模型中是阻塞的。Golang 實現的 CSP 模型,channel 是一個阻塞隊列,當阻塞隊列已滿的時候,向 channel 中發送數據,會導致發送消息的協程阻塞。

第三個區別則是關于消息送達的。在《42 | Actor 模型:面向對象原生的并發模型》這篇文章中,我們介紹過 Actor 模型理論上不保證消息百分百送達,而在 Golang 實現的 CSP 模型中,是能保證消息百分百送達的。不過這種百分百送達也是有代價的,那就是有可能會導致死鎖。
比如,下面這段代碼就存在死鎖問題,在主協程中,我們創建了一個無緩沖的 channel ch,然后從 ch 中接收數據,此時主協程阻塞,main() 方法中的主協程阻塞,整個應用就阻塞了。這就是 Golang 中最簡單的一種死鎖。

func main() {// 創建一個無緩沖的channel ch := make(chan int)// 主協程會阻塞在此處,發生死鎖<- ch }

總結

Golang 中雖然也支持傳統的共享內存的協程間通信方式,但是推薦的還是使用 CSP 模型,以通信的方式共享內存。
Golang 中實現的 CSP 模型功能上還是很豐富的,例如支持 select 語句,select 語句類似于網絡編程里的多路復用函數 select(),只要有一個 channel 能夠發送成功或者接收到數據就可以跳出阻塞狀態。鑒于篇幅原因,我就點到這里,不詳細介紹那么多了。

CSP 模型是托尼·霍爾(Tony Hoare)在 1978 年提出的,不過這個模型這些年一直都在發展,其理論遠比 Golang 的實現復雜得多,如果你感興趣,可以參考霍爾寫的Communicating Sequential Processes這本電子書。另外,霍爾在并發領域還有一項重要成就,那就是提出了霍爾管程模型,這個你應該很熟悉了,Java 領域解決并發問題的理論基礎就是它。
Java 領域可以借助第三方的類庫JCSP來支持 CSP 模型,相比 Golang 的實現,JCSP 更接近理論模型,如果你感興趣,可以下載學習。不過需要注意的是,JCSP 并沒有經過廣泛的生產環境檢驗,所以并不建議你在生產環境中使用。
?

總結

以上是生活随笔為你收集整理的Java并发编程实战~协程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。