欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)激活碼778899分享:Kotlin多線程

柚子快報(bào)激活碼778899分享:Kotlin多線程

http://yzkb.51969.com/

目錄

線程的使用

線程的創(chuàng)建

例一:創(chuàng)建線程并輸出Hello World

Thread對(duì)象的用法

start()

join()

interrupt()

線程安全

原子性

可見性

有序性

線程鎖

ReentrantLock

ReadWriteLock

線程的使用

Java虛擬機(jī)中的多線程可以1:1映射至CPU中,即一個(gè)CPU線程跑一個(gè)任務(wù),這叫并行,也可以N:1地運(yùn)行,即一個(gè)CPU線程交替跑多個(gè)任務(wù),看起來是同時(shí)地。這兩種方法都叫并發(fā)

線程的創(chuàng)建

kotlin中,可以通過kotlin.concurrent包下的thread函數(shù)創(chuàng)建一個(gè)線程:

fun thread(

start: Boolean = true,

isDaemon: Boolean = false,

contextClassLoader: ClassLoader? = null,

name: String? = null,

priority: Int = -1,

block: () -> Unit

): Thread

該函數(shù)接收6個(gè)參數(shù),必須定義block參數(shù),因?yàn)樗蔷€程的執(zhí)行函數(shù):

start: 如果為真,則立即執(zhí)行isDaemon: 如果為真,則會(huì)創(chuàng)建守護(hù)線程。當(dāng)所有正在運(yùn)行的線程都是守護(hù)線程時(shí),Java虛擬機(jī)將自動(dòng)退出contextClassLoader: 線程中所使用的類加載器,又叫上下文類加載器。如果不指定類加載器,則會(huì)使用系統(tǒng)的類加載器name: 線程的名字priority: 線程的優(yōu)先級(jí)。只有該參數(shù)大于0時(shí)才有效。線程的優(yōu)先級(jí)在1-10之間,默認(rèn)為5. 線程的優(yōu)先級(jí)的最大值、最小值、默認(rèn)值被分別定義在java.long包下Thread類的靜態(tài)變量MAX_PRIORITY、MIN_PRIORITY和NORM_PRIORITY內(nèi)block: 一個(gè)回調(diào)函數(shù),無參數(shù),無返回值,線程運(yùn)行調(diào)用此方法

該函數(shù)返回一個(gè)java.long包下的Thread對(duì)象,表示創(chuàng)建的線程。

因?yàn)樵摵瘮?shù)的前五個(gè)參數(shù)都有默認(rèn)值,因此可以使用kotlin的語法糖,簡(jiǎn)化thread的用法:

thread {

println("Hello World")

}

例一:創(chuàng)建線程并輸出Hello World

import kotlin.concurrent.thread

fun main() {

thread {

println("Hello World")

}

}

這就是這個(gè)例子的全部代碼了,是不是非常簡(jiǎn)單?

在main方法里,創(chuàng)建了一個(gè)線程,線程執(zhí)行時(shí)打印Hello World.

Thread對(duì)象的用法

我們提到,thread函數(shù)會(huì)返回一個(gè)Thread對(duì)象,那么,如何使用這個(gè)Thread對(duì)象呢?

首先,Thread類是用Java寫的,所以它的函數(shù)原型是Java形式的

start()

Thread對(duì)象中有start()方法,表示執(zhí)行線程:

public void start()

如果我們?cè)趖hread方法中設(shè)置start參數(shù)為false,那么我們可以通過調(diào)用start()方法執(zhí)行線程:

import kotlin.concurrent.thread

fun main() {

val th = thread(start = false) {

println("Hello World")

}

println("準(zhǔn)備啟動(dòng)線程")

th.start()

}

執(zhí)行結(jié)果:

準(zhǔn)備啟動(dòng)線程

Hello World

join()

join()方法等待線程執(zhí)行結(jié)束:

public final void join()

throws InterruptedException

如我們可以這樣使用:

import kotlin.concurrent.thread

fun main() {

val th = thread {

Thread.sleep(1000)

println("th執(zhí)行完成")

}

th.join()

println("main執(zhí)行完成")

}

?執(zhí)行結(jié)果如下:

th執(zhí)行完成

main執(zhí)行完成

因此,join成功是main線程等待th線程結(jié)束

如果我們?nèi)サ魌h.join(),則輸出:

main執(zhí)行完成

th執(zhí)行完成

這就是join()的基本用法

另外,如果當(dāng)前線程(調(diào)用join()方法的線程)被任何線程中斷,則拋出InterruptedException

異常,并不再等待:

import kotlin.concurrent.thread

fun main() {

val th = thread {

val th2 = thread {

Thread.sleep(1000)

println("th2執(zhí)行完成")

}

try {

th2.join()

}catch (e: InterruptedException){

println("中斷")

}

println("th執(zhí)行完成")

}

th.interrupt()

}

?執(zhí)行結(jié)果:

中斷

th執(zhí)行完成

th2執(zhí)行完成

因?yàn)閙ain線程創(chuàng)建了th線程,th線程又創(chuàng)建了th2線程。th線程調(diào)用join()方法等待th2線程時(shí),main線程中斷了th線程,因此th線程中的join()方法停止等待,執(zhí)行完成。之后,th2線程才執(zhí)行完成

interrupt()

interrupt()中斷線程。調(diào)用該方法時(shí),將會(huì)把指定線程的Thread.interrupted()方法的返回值設(shè)為true,因此,要中斷線程需要檢測(cè)這個(gè)值。

public void interrupt()

?其用法如下:

import kotlin.concurrent.thread

fun main() {

val th = thread {

while (true){

if (Thread.interrupted()) break

}

println("th被中斷")

}

Thread.sleep(1000)

println("準(zhǔn)備中斷線程")

th.interrupt()

}

輸出:

準(zhǔn)備中斷線程

th被中斷

線程安全

線程安全必須同時(shí)滿足原子性、可見性和有序性:

原子性

考慮這么一個(gè)代碼:

import kotlin.concurrent.thread

fun main() {

var tmp = 0

val th1 = thread {

Thread.sleep(200)

tmp++

}

val th2 = thread {

Thread.sleep(200)

tmp++

}

th1.join()

th2.join()

println(tmp)

}

其中,tmp被增加了2次,因此應(yīng)該返回2,可是我的輸出結(jié)果為:

1

這是為什么呢?

我們知道,自增語句分三步:讀取、增加、寫入。在兩個(gè)線程同時(shí)執(zhí)行的時(shí)候,可能會(huì)出現(xiàn)類似以下情況:

時(shí)間第一個(gè)線程第二個(gè)線程1讀取tmp變量(0)2計(jì)算tmp+1的值3讀取tmp變量(0)4寫入tmp+1的值到tmp變量(1)5計(jì)算tmp+1的值6寫入tmp+1的值到tmp變量(1)

因此,由于線程之間并發(fā)運(yùn)行,最終tmp的值為1。

之所以自增語句會(huì)出現(xiàn)這樣的問題,是因?yàn)樽栽稣Z句需要3塊時(shí)間才能完成,不能一口氣直接完成。如果自增可以直接完成,在非并行的情況下,就會(huì)出現(xiàn)以下情況:

時(shí)間第一個(gè)線程第二個(gè)線程1tmp自增2tmp自增

這樣就不會(huì)有沖突了。

我們稱這種直接完成而不被其他線程打斷的操作叫原子操作,在kotlin中可以通過java.util.concurrent.atomic定義的支持原子操作的類,實(shí)現(xiàn)原子操作:

import java.util.concurrent.atomic.AtomicInteger

import kotlin.concurrent.thread

fun main() {

val tmp = AtomicInteger(0)

val th1 = thread {

Thread.sleep(200)

tmp.incrementAndGet()

}

val th2 = thread {

Thread.sleep(200)

tmp.incrementAndGet()

}

th1.join()

th2.join()

println(tmp.get())

}

注意:原子操作不適合并行時(shí)的問題,但由于現(xiàn)代電腦CPU少線程多的現(xiàn)狀,大部分的情況都可以使用原子操作:

一個(gè)12核CPU有將近4000個(gè)線程

可見性

由于現(xiàn)代設(shè)備的線程有自己的緩存,有些時(shí)候當(dāng)一個(gè)變量被修改后,其他線程可能看不到修改的信息,因此就會(huì)產(chǎn)生線程安全問題:

import kotlin.concurrent.thread

fun main() {

var boolean = true

val th1 = thread {

Thread.sleep(200)

boolean = false

println("已經(jīng)將boolean設(shè)為false")

}

val th2 = thread {

println("等待boolean為false")

while (boolean){}

}

th1.join()

th2.join()

println("線程執(zhí)行完畢")

}

執(zhí)行結(jié)果:

等待boolean為false

已經(jīng)將boolean設(shè)為false

(無限循環(huán))

?這是因?yàn)?,?dāng)boolean被修改時(shí),th2不能及時(shí)獲得boolean的變化,所以跳不出循環(huán),出現(xiàn)了可見性問題。我們可以通過Thread.yield()方法同步變量在線程內(nèi)和進(jìn)程內(nèi)的數(shù)據(jù):

import kotlin.concurrent.thread

fun main() {

var boolean = true

val th1 = thread {

Thread.sleep(200)

boolean = false

println("已經(jīng)將boolean設(shè)為false")

}

val th2 = thread {

println("等待boolean為false")

while (boolean){

Thread.yield()

}

}

th1.join()

th2.join()

println("線程執(zhí)行完畢")

}

執(zhí)行結(jié)果:

等待boolean為false

已經(jīng)將boolean設(shè)為false

線程執(zhí)行完畢

注意,Thread.yield()方法的真實(shí)作用是告訴調(diào)度器當(dāng)前線程愿意放棄對(duì)處理器的使用,直到處理器重新調(diào)用這個(gè)線程,可以用以下表格來說明:

因此,Thread.yield()方法就可以抽空在合適的時(shí)機(jī)同步變量的數(shù)據(jù),實(shí)現(xiàn)線程的可見性。

我們前面舉的變量自增的例子也有可能是因?yàn)榫€程的可見性問題導(dǎo)致的。

有序性

我們?cè)趯懘a時(shí),往往認(rèn)為程序是按順序運(yùn)行的,其實(shí)并不是。如果前后兩個(gè)指令沒有任何關(guān)聯(lián),處理器可能會(huì)先運(yùn)行寫在后面的省時(shí)指令,后運(yùn)行寫在前面的費(fèi)時(shí)指令,這樣可以起到節(jié)省資源的效果。在單線程中,這沒有問題,但在多線程中,就出現(xiàn)了問題:

import kotlin.concurrent.thread

fun main() {

var a = 0

var b = 0

var x = -1

var y = -1

var count = 0

while (true) {

a = 0

b = 0

x = -1

y = -1

val th1 = thread {

b = 1

x = a

return@thread

}

val th2 = thread {

a = 1

y = b

return@thread

}

th1.join()

th2.join()

count++

if (x == 0 && y == 0){

println("第$count 次,($x,$y)")

break

}

}

}

輸出:

第100010 次,(0,0)

按照正常的邏輯,這個(gè)程序的運(yùn)行過程應(yīng)該是類似這樣的:

時(shí)間第一個(gè)線程第二個(gè)線程1b=12a=13x=a(1)4y=b(1)

時(shí)間第一個(gè)線程第二個(gè)線程1b=12x=a(0)3a=14y=b(1)

時(shí)間第一個(gè)線程第二個(gè)線程1a=12y=b(0)3b=14x=a(1)

無論如何,x和y都不可能同時(shí)為0,可是為什么原程序中,x和y都為0呢?

只有一種可能,類似這樣:

x和y的賦值語句被處理器提到了前面,因此出現(xiàn)了有序性的問題?

@Volatile注解可以保證指定變量的可見性和有序性:

import kotlin.concurrent.thread

@Volatile

var a = 0

@Volatile

var b = 0

@Volatile

var x = -1

@Volatile

var y = -1

fun main() {

var count = 0

while (true) {

a = 0

b = 0

x = -1

y = -1

val th1 = thread {

b = 1

x = a

return@thread

}

val th2 = thread {

a = 1

y = b

return@thread

}

th1.join()

th2.join()

count++

if (x == 0 && y == 0) {

println("第$count 次,($x,$y)")

break

}

}

}

運(yùn)行結(jié)果:

(無限循環(huán))

?可見,@Volatile注解保證了其有序性。這個(gè)注解保證可見性和有序性的原理如下:

可見性:給變量上一個(gè)Load屏障,每次讀取數(shù)據(jù)的時(shí)候被強(qiáng)制從進(jìn)程中讀取最新的數(shù)據(jù);同時(shí)上一個(gè)Store屏障,強(qiáng)制使每次修改之后強(qiáng)制刷新進(jìn)程中的數(shù)據(jù)有序性:通過禁止重排屏障禁止指令重排:

StoreStore屏障:禁止StoreStore屏障的前后Store寫操作重排LoadLoad屏障:禁止LoadLoad屏障的前后Load讀操作進(jìn)行重排LoadStore屏障:禁止LoadStore屏障的前面Load讀操作跟LoadStore屏障后面的Store寫操作重排StoreLoad屏障:禁止LoadStore屏障前面的Store寫操作跟后面的Load/Store 讀寫操作重排

線程鎖

我們可以通過線程鎖保證線程安全:

如果在操作對(duì)象之前,線程先聲明:“這個(gè)對(duì)象是我的”,當(dāng)另一個(gè)線程也想操作這個(gè)對(duì)象時(shí),發(fā)現(xiàn)已經(jīng)有人聲明過了,那么它就等待,直到那個(gè)發(fā)布聲明的線程又發(fā)了一個(gè)“這個(gè)對(duì)象不是我的了”的聲明。當(dāng)然,這兩個(gè)聲明其實(shí)起到了一個(gè)鎖的作用,當(dāng)聲明“這個(gè)對(duì)象是我的”時(shí),對(duì)象就被上了鎖,當(dāng)聲明“這個(gè)對(duì)象不是我的了”時(shí),對(duì)象的鎖就被解開了

當(dāng)然,上鎖和解鎖這一過程都必須保證原子性、可見性和有序性

我們可以如下修改代碼:

import kotlin.concurrent.thread

class MyMutex{

private var mutex: Boolean = false

@Synchronized

fun lock(){

while (mutex){} // 等待解鎖

mutex = true // 上鎖

return

}

@Synchronized

fun unlock(){

mutex = false // 解鎖

return

}

}

fun main() {

var tmp = 0

val mutex = MyMutex()

val th1 = thread {

Thread.sleep(200)

mutex.lock()

tmp++

mutex.unlock()

}

val th2 = thread {

Thread.sleep(200)

mutex.lock()

tmp++

mutex.unlock()

}

th1.join()

th2.join()

println(tmp)

}

這里面使用了@Synchronized注解,可以保證方法的原子性、可見性和有序性。在這里面保證了上鎖和解鎖的原子性、可見性和有序性。

@Synchronized注解是這么保證方法的原子性、可見性和有序性的:

原子性:在方法執(zhí)行前加鎖,執(zhí)行后解鎖,這樣一個(gè)方法同時(shí)只能有一個(gè)線程使用可見性:在方法執(zhí)行時(shí)給方法內(nèi)的每個(gè)變量上一個(gè)Load屏障,每次讀取數(shù)據(jù)的時(shí)候被強(qiáng)制從進(jìn)程中讀取最新的數(shù)據(jù);同時(shí)上一個(gè)Store屏障,強(qiáng)制使每次修改之后強(qiáng)制刷新進(jìn)程中的數(shù)據(jù)有序性:通過禁止重排屏障禁止指令重排:

StoreStore屏障:禁止StoreStore屏障的前后Store寫操作重排LoadLoad屏障:禁止LoadLoad屏障的前后Load讀操作進(jìn)行重排LoadStore屏障:禁止LoadStore屏障的前面Load讀操作跟LoadStore屏障后面的Store寫操作重排StoreLoad屏障:禁止LoadStore屏障前面的Store寫操作跟后面的Load/Store 讀寫操作重排

當(dāng)然,我們上面的代碼只是簡(jiǎn)單實(shí)現(xiàn)了一個(gè)線程鎖,kotlin中可以使用自帶的線程鎖:

ReentrantLock

ReentrantLock是一個(gè)遞歸互斥,在Java中叫可重入鎖,允許同一個(gè)線程多次上鎖,相應(yīng)的,同一個(gè)線程上鎖多少次,就要解鎖多少次。為什么要允許線程多次上鎖呢?

我們來看以下代碼:

import kotlin.concurrent.thread

class MyMutex{

private var mutex: Boolean = false

@Synchronized

fun lock(){

while (mutex){} // 等待解鎖

mutex = true // 上鎖

return

}

@Synchronized

fun unlock(){

mutex = false // 解鎖

return

}

}

fun main() {

val mutex1 = MyMutex()

val mutex2 = MyMutex()

val th1 = thread {

mutex1.lock()

println("th1 locked mutex1") // 模擬操作受mutex1保護(hù)的資源

Thread.sleep(200)

mutex2.lock()

println("th1 locked mutex2") // 模擬操作受mutex2保護(hù)的資源

mutex1.unlock()

println("th1 unlocked mutex1")

Thread.sleep(200)

mutex2.unlock()

println("th2 unlocked mutex2")

return@thread

}

val th2 = thread {

mutex2.lock()

println("th1 locked mutex2") // 模擬操作受mutex2保護(hù)的資源

Thread.sleep(200)

mutex1.lock()

println("th1 locked mutex1") // 模擬操作受mutex1保護(hù)的資源

mutex2.unlock()

println("th1 unlocked mutex2")

Thread.sleep(200)

mutex1.unlock()

println("th2 unlocked mutex1")

return@thread

}

th1.join()

th2.join()

println("線程執(zhí)行完畢")

}

運(yùn)行結(jié)果:

th1 locked mutex1

th1 locked mutex2

(無限循環(huán))

為什么會(huì)無限循環(huán)呢?因?yàn)閠h1鎖定mutex1后想要鎖定mutex2,卻發(fā)現(xiàn)mutex2被th2鎖定;而th2鎖定mutex2后想要鎖定mutex1,卻發(fā)現(xiàn)mutex1被th1鎖定,因此出現(xiàn)了無限循環(huán)的問題。我們稱這種問題為死鎖

使用遞歸互斥可以有效避免死鎖問題:

import java.util.concurrent.locks.ReentrantLock

import kotlin.concurrent.thread

fun main() {

val mutex = ReentrantLock()

val th1 = thread {

mutex.lock()

println("th1 locked mutex1")

Thread.sleep(200) // 模擬操作受mutex1保護(hù)的資源

mutex.lock()

println("th1 locked mutex2") // 模擬操作受mutex2保護(hù)的資源

mutex.unlock()

println("th1 unlocked mutex1")

Thread.sleep(200)

mutex.unlock()

println("th2 unlocked mutex2")

return@thread

}

val th2 = thread {

mutex.lock()

println("th1 locked mutex2") // 模擬操作受mutex2保護(hù)的資源

Thread.sleep(200)

mutex.lock()

println("th1 locked mutex1") // 模擬操作受mutex1保護(hù)的資源

mutex.unlock()

println("th1 unlocked mutex2")

Thread.sleep(200)

mutex.unlock()

println("th2 unlocked mutex1")

return@thread

}

th1.join()

th2.join()

println("線程執(zhí)行完畢")

}

其中,代碼

val mutex = ReentrantLock()

表示創(chuàng)建一個(gè)可重入鎖,這個(gè)對(duì)象的lock()和unlock()方法分別表示上鎖和解鎖

柚子快報(bào)激活碼778899分享:Kotlin多線程

http://yzkb.51969.com/

精彩內(nèi)容

評(píng)論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18605399.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄