自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

白話說Java線程(一)之讓線程先跑起來

開發(fā) 開發(fā)工具
進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進(jìn)程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是程序的基本執(zhí)行實(shí)體;在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器。程序是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實(shí)體。

[[210357]]

一、什么是多線程

要理解什么是多線程,先理清楚什么是進(jìn)程,什么是線程。

先看看百度百科上如何解釋進(jìn)程的概念:

進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進(jìn)程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是程序的基本執(zhí)行實(shí)體;在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器。程序是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實(shí)體。

那么線程就可以被理解成進(jìn)程中可獨(dú)立運(yùn)行的子任務(wù)。

既然是在一個(gè)進(jìn)程內(nèi)獨(dú)立運(yùn)行的子任務(wù),那么單進(jìn)程意思就是當(dāng)前進(jìn)程只能同時(shí)允許一個(gè)進(jìn)程在運(yùn)行,而多進(jìn)程可以允許多個(gè)進(jìn)程間來回切換,進(jìn)而更快的完成更多的任務(wù)。

舉個(gè)例子:

有一把錘子,兩家人共用。小明家需要用這把錘子打造一個(gè)柜子(需要一天時(shí)間),小新家只需要用這個(gè)錘子在墻上釘一個(gè)釘子(最多五分鐘)。既然是共用的錘子,而兩家人都需要使用,在單線程的環(huán)境下,如果想小明家先拿到錘子,那么就需要獨(dú)占一天的時(shí)間,小新家雖然只需要使用五分鐘,但是卻要等一天時(shí)間小明家才會(huì)把錘子空出來。而在多線程的環(huán)境下,雖然小明家先拿到錘子,但是也不是一天都在使用,中間也需要吃飯,也需要等待其他材料到齊,總有錘子空閑的時(shí)期,這個(gè)時(shí)候可以先讓小新家拿去使用五分鐘,然后使用完歸還小明家繼續(xù)使用。

在多進(jìn)程的環(huán)境下,CPU 完全可以在兩個(gè)任務(wù)間來回切換,使耗時(shí)短的任務(wù)不致于等待耗時(shí)長(zhǎng)的任務(wù)完成才能得到執(zhí)行,系統(tǒng)的運(yùn)行效率將大大的得到提升。

但是需要注意的是,凡事都有一個(gè)度,雖然多線程間切換任務(wù)可以加快多個(gè)任務(wù)執(zhí)行的效率,但是同時(shí),在切換任務(wù)的時(shí)候,也是有一定的開銷的,頻繁的切換任務(wù)可能切換任務(wù)消耗的時(shí)間會(huì)更多。

二、使用多線程

在 Java 的 JDK 中,已經(jīng)存在來對(duì)多線程技術(shù)的支持,如果想使用多線程,有兩種方式:

繼承 Thread 類。

實(shí)現(xiàn) Runnable 接口。

可以看到 Thread 是一個(gè) class,而 Runnable 是一個(gè) Interface 。由于 Java 的單繼承限制,如果有更靈活的繼承要求,可以考慮實(shí)現(xiàn) Runnable 。但是不管是實(shí)用那種方式,都需要一個(gè) Thread 對(duì)象來承載。

這里為來偷懶,直接用一個(gè) Android 項(xiàng)目來講解來。可以看到,實(shí)用起來非常的簡(jiǎn)單,只需要實(shí)現(xiàn)它們的 run() 方法,然后在需要的時(shí)候,調(diào)用 Thread.start() 方法即可。

1、start()和run()有什么不同

那么問題來來,既然我們重寫的是 run() 方法,但是為什么最終調(diào)用的卻是 start() 方法?

Thread 這個(gè)類中的 start() 方法就是通知「線程調(diào)度器」此線程已經(jīng)準(zhǔn)備就緒,隨時(shí)等待創(chuàng)建好線程,并且調(diào)用 run() 方法執(zhí)行。這樣的啟動(dòng)線程,由「線程調(diào)度器」來調(diào)用 run() 方法,具有異步執(zhí)行的效果。而如果我們直接調(diào)用 run() 方法的話,就只是和調(diào)用一個(gè)普通方法一樣,是在當(dāng)前線程的同步執(zhí)行。

2、線程的執(zhí)行是無序的

雖然我們啟動(dòng)線程去執(zhí)行的代碼,可以有先后,但是實(shí)際上,線程的執(zhí)行是無序的。也就是說,線程被執(zhí)行的順序是隨機(jī)的,看誰運(yùn)氣好。

上面例子中,分別在各個(gè)階段輸出來對(duì)應(yīng)的 Log,可以看到,其實(shí)它們執(zhí)行的順序是無序的。

三、線程安全

已經(jīng)講解完線程的基本使用,如果多條線程之間,沒有任何共享的實(shí)例對(duì)象被改變,那么到這里就算是完結(jié)了。

但是實(shí)際情況來說,并不是這樣的。實(shí)際上,我們經(jīng)常會(huì)使用多線程的技術(shù)操作同一個(gè)實(shí)例變量。在多個(gè)線程之間,操作同一個(gè)實(shí)例變量,就變成了多線程的技術(shù)難點(diǎn),如何保持多個(gè)線程能準(zhǔn)確并且安全的操作到同一個(gè)實(shí)例變量,這就是線程安全。

拿上面的錘子的例子來說,如果錘子是由物業(yè)來保管的,這個(gè)時(shí)候在物業(yè)的管理系統(tǒng)上顯示錘子是有一個(gè)的,這個(gè)小明家和小新家都來借錘子。兩個(gè)不同的物業(yè)管理員在同一個(gè)系統(tǒng)中查到還有一個(gè)錘子,就同時(shí)做了一個(gè)出庫的動(dòng)作。這個(gè)時(shí)候就看物業(yè)系統(tǒng)是如何設(shè)計(jì)的了。

使用計(jì)數(shù)自減的方式,那么就會(huì)在錘子的庫存上出現(xiàn) -1 的計(jì)數(shù)。

使用直接改變庫存的方式,那么雖然兩個(gè)物業(yè)管理員都會(huì)講庫存從 1 改為 0,這個(gè)時(shí)候庫存數(shù)據(jù)是對(duì)的。

雖然這兩種方式,看著第二種在賬面上庫存數(shù)據(jù)是正確的,而第一種出現(xiàn)了負(fù)數(shù)的情況。但是實(shí)際上當(dāng)兩個(gè)物業(yè)管理員真的去庫房取錘子的時(shí)候,一定有一個(gè)是取不到的,就看這個(gè)時(shí)候誰運(yùn)氣好了。這就是一個(gè)典型的「非線程安全問題」。

這個(gè)時(shí)候就需要考慮線程安全的問題,在同時(shí)操作同一個(gè)實(shí)例對(duì)象的時(shí)候,需要保證數(shù)據(jù)一致性。

簡(jiǎn)單的保證線程安全,最常用的方式,就是使用 synchronized 關(guān)鍵字。synchronized 可以被加在任意的對(duì)象或者方法上,表示對(duì)這個(gè)方法或者對(duì)象加鎖,而加鎖的這段代碼被稱為「互斥區(qū)」。當(dāng)一個(gè)線程想要執(zhí)行 synchronized 中的代碼的時(shí)候,會(huì)首先嘗試拿這把鎖,如果能夠拿到的話,就可以繼續(xù)執(zhí)行 synchronized 內(nèi)的代碼。如果拿不到的話,就會(huì)不斷嘗試去拿這把鎖,直到能拿到并且完成執(zhí)行為止。當(dāng)獲取到鎖并且執(zhí)行完 synchronized 中的代碼之后,就會(huì)放棄鎖的持有,以便于其他線程能得到鎖。

下面舉個(gè)例子來說明使用方法。

new 五個(gè)線程來操作同一個(gè)計(jì)數(shù),然后對(duì)齊進(jìn)行自減,并輸出 Log ,先看看沒有加 synchronized 的情況。

可以看到,線程指定的順序是無章的,輸出的 count 的值也是無序的。

如果想讓他們有序的輸出可以使用 synchronized 來標(biāo)記 run() 方法。

這個(gè)時(shí)候,雖然線程的執(zhí)行順序依然是無需的,但是卻可以保證對(duì)數(shù)據(jù)的處理是有序進(jìn)行的。

四、Thread的一些其他方法

Thread 中還提供了一些其他的API,可以供我們使用,這里簡(jiǎn)單的介紹一下。

1、currentThread()

currentThread() 方法啊可以獲取到當(dāng)前代碼正運(yùn)行的線程的信息。

從簽名可以看到,它是一個(gè)靜態(tài)的方法,可以在任何地方調(diào)用它。

2、isAlive()

isAlive() 方法用于判斷當(dāng)前指定線程數(shù)否處于活動(dòng)狀態(tài)?;顒?dòng)狀態(tài)并不僅僅指的是在運(yùn)行的狀態(tài),只要是已經(jīng)準(zhǔn)備運(yùn)行并且沒有結(jié)束運(yùn)行,使用 isAlive() 都會(huì)返回 true。

也就是說,在調(diào)用了 Thread.start() 之后,一直到 Thread.run() 執(zhí)行完成之前,isAlive()都是 true。

3、sleep()

sleep() 方法和名稱一樣,可以使當(dāng)前進(jìn)程休眠指定的毫秒數(shù)。

它也是一個(gè) static 的方法,可以直接調(diào)用,用于休眠當(dāng)前運(yùn)行的線程。注意 sleep() 方法可能會(huì)拋出一個(gè) InterruptedException 的異常,需要我們對(duì)其 catch 住。

4、getId()

雖然在 new Thread() 的時(shí)候,可以通過構(gòu)造方法對(duì) Thread 指定一個(gè)名稱,但是名稱是可以重復(fù)的,如果要唯一確定一個(gè) Thread 的 ID,可以使用 getId() 來獲取一個(gè)線程的唯一標(biāo)志。

五、結(jié)語

今天簡(jiǎn)單介紹了如何正確的開啟一個(gè)線程和包裝開啟的線程是安全的,之后再講如何優(yōu)雅的停止一個(gè)線程。

【本文為51CTO專欄作者“張旸”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過微信公眾號(hào)聯(lián)系作者獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2017-11-22 15:11:33

Java線程停止

2011-05-04 11:26:47

優(yōu)化

2021-01-22 14:03:34

Flutter系統(tǒng)鴻蒙

2021-01-12 11:12:58

大數(shù)據(jù)智慧交通

2009-04-29 14:40:17

2023-03-02 23:09:53

Node.jsC++JS

2010-07-13 09:31:08

RubyRuby on Rai

2019-09-03 08:00:00

電腦硬盤程序

2024-05-27 09:01:22

2020-04-06 09:05:07

谷歌機(jī)器狗人工智能

2023-08-03 09:02:32

LangChain開發(fā)GLM

2022-12-06 09:03:44

代碼fork系統(tǒng)

2015-08-04 17:46:19

戴爾anycloud云計(jì)算

2022-01-10 10:23:07

瀏覽器Vitenode

2012-05-15 13:29:20

HTML5

2021-11-10 10:00:48

鴻蒙HarmonyOS應(yīng)用

2023-01-31 07:42:29

代碼JDKMaven

2014-04-18 17:12:00

樂跑手環(huán)

2019-09-08 23:00:19

GitHub代碼開發(fā)者

2019-03-21 15:00:47

Python程序代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)