精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Android性能優(yōu)化之Java線程機(jī)制與線程調(diào)度原理詳解

移動(dòng)開發(fā) Android
在平時(shí)工作中如若使用不當(dāng)會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂、執(zhí)行效率低(還不如單線程去運(yùn)行)或者死鎖程序掛掉等等問題,所以掌握了解多線程至關(guān)重要。

[[414446]]

本文轉(zhuǎn)載自微信公眾號(hào)「Android開發(fā)編程」,作者Android開發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開發(fā)編程公眾號(hào)。

前言小計(jì)

在平時(shí)工作中如若使用不當(dāng)會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂、執(zhí)行效率低(還不如單線程去運(yùn)行)或者死鎖程序掛掉等等問題,所以掌握了解多線程至關(guān)重要;

線程有很多優(yōu)勢(shì):

1、提高多處理器的利用效率;

2、簡化業(yè)務(wù)功能設(shè)計(jì);

3、實(shí)現(xiàn)異步處理;

多線程的風(fēng)險(xiǎn):

1、共享數(shù)據(jù)的線程安全性;

2、多線程執(zhí)行時(shí)的活躍性問題;

3、多線程的所帶來的性能損失問題;

多線程相對(duì)于其他知識(shí)點(diǎn)來講,有一定的學(xué)習(xí)門檻,并且了解起來比較費(fèi)勁;

線程的優(yōu)勢(shì)我們很清楚,線程的風(fēng)險(xiǎn)我們也都知道,但是要做好風(fēng)險(xiǎn)控制就沒有那么簡單了;

本文從基礎(chǔ)概念開始到最后的并發(fā)模型由淺入深,講解下線程方面的知識(shí);

一、什么是線程?

1、線程簡介

  • 線程是進(jìn)程中可獨(dú)立執(zhí)行的最小單位,也是 CPU 資源分配的基本單位;
  • 進(jìn)程是程序向操作系統(tǒng)申請(qǐng)資源的基本條件,一個(gè)進(jìn)程可以包含多個(gè)線程,同一個(gè)進(jìn)程中的線程可以共享進(jìn)程中的資源,如內(nèi)存空間和文件句柄;
  • 操作系統(tǒng)會(huì)把資源分配給進(jìn)程,但是 CPU 資源比較特殊,它是分配給線程的,這里說的 CPU 資源也就是 CPU 時(shí)間片;
  • 進(jìn)程與線程的關(guān)系,就像是飯店與員工的關(guān)系,飯店為顧客提供服務(wù),而提供服務(wù)的具體方式是通過一個(gè)個(gè)員工實(shí)現(xiàn)的;
  • 線程的作用是執(zhí)行特定任務(wù),這個(gè)任務(wù)可以是下載文件、加載圖片、繪制界面等;
  • 下面我們就來看看線程的四個(gè)屬性、六個(gè)方法以及六種狀態(tài);

2、線程的四個(gè)屬性

線程有編號(hào)、名字、類別以及優(yōu)先級(jí)四個(gè)屬性,除此之外,線程的部分屬性還具有繼承性,下面我們就來看看線程的四個(gè)屬性的作用和線程的繼承性;

①編號(hào)

線程的編號(hào)(id)用于標(biāo)識(shí)不同的線程,每條線程擁有不同的編號(hào);

注意事項(xiàng):不能作為唯一標(biāo)識(shí),某個(gè)編號(hào)的線程運(yùn)行結(jié)束后,該編號(hào)可能被后續(xù)創(chuàng)建的線程使用,因此編號(hào)不適合用作唯一標(biāo)識(shí),編號(hào)是只讀屬性,不能修改;

②名字

  • 每個(gè)線程都有自己的名字(name),名字的默認(rèn)值是 Thread-線程編號(hào),比如 Thread-0 ;
  • 除了默認(rèn)值,我們也可以給線程設(shè)置名字,以我們自己的方式去區(qū)分每一條線程;
  • 作用:給線程設(shè)置名字可以讓我們?cè)谀硹l線程出現(xiàn)問題時(shí),用該線程的名字快速定位出問題的地方

③類別

  • 線程的類別(daemon)分為守護(hù)線程和用戶線程,我們可以通過 setDaemon(true) 把線程設(shè)置為守護(hù)線程;
  • 當(dāng) JVM 要退出時(shí),它會(huì)考慮是否所有的用戶線程都已經(jīng)執(zhí)行完畢,是的話則退出;
  • 而對(duì)于守護(hù)線程,JVM 在退出時(shí)不會(huì)考慮它是否執(zhí)行完成;
  • 作用:守護(hù)線程通常用于執(zhí)行不重要的任務(wù),比如監(jiān)控其他線程的運(yùn)行情況,GC 線程就是一個(gè)守護(hù)線程;
  • 注意事項(xiàng):setDaemon() 要在線程啟動(dòng)前設(shè)置,否則 JVM 會(huì)拋出非法線程狀態(tài)異常(IllegalThreadStateException);

④優(yōu)先級(jí)

作用:線程的優(yōu)先級(jí)(Priority)用于表示應(yīng)用希望優(yōu)先運(yùn)行哪個(gè)線程,線程調(diào)度器會(huì)根據(jù)這個(gè)值來決定優(yōu)先運(yùn)行哪個(gè)線程;

⑤取值范圍

Java 中線程優(yōu)先級(jí)的取值范圍為 1~10,默認(rèn)值是 5,Thread 中定義了下面三個(gè)優(yōu)先級(jí)常量;

  • 最低優(yōu)先級(jí):MIN_PRIORITY = 1;
  • 默認(rèn)優(yōu)先級(jí):NORM_PRIORITY = 5;
  • 最高優(yōu)先級(jí):MAX_PRIORITY = 10;

注意事項(xiàng):不保證,線程調(diào)度器把線程的優(yōu)先級(jí)當(dāng)作一個(gè)參考值,不一定會(huì)按我們?cè)O(shè)定的優(yōu)先級(jí)順序執(zhí)行線程;

⑥線程饑餓

優(yōu)先級(jí)使用不當(dāng)會(huì)導(dǎo)致某些線程永遠(yuǎn)無法執(zhí)行,也就是線程饑餓的情況;

⑦繼承性

線程的繼承性指的是線程的類別和優(yōu)先級(jí)屬性是會(huì)被繼承的,線程的這兩個(gè)屬性的初始值由開啟該線程的線程決定;

假如優(yōu)先級(jí)為 5 的守護(hù)線程 A 開啟了線程 B,那么線程 B 也是一個(gè)守護(hù)線程,而且優(yōu)先級(jí)也是 5 ;

這時(shí)我們就把線程 A 叫做線程 B 的父線程,把線程 B 叫做線程 A 的子線程;

3、線程的六個(gè)重要方法

線程的常用方法有六個(gè),它們分別是三個(gè)非靜態(tài)方法 start()、run()、join() 和三個(gè)靜態(tài)方法 currentThread()、yield()、sleep() ;

下面我們就來看下這六個(gè)方法都有哪些作用和注意事項(xiàng)

①start()

  • 作用:start() 方法的作用是啟動(dòng)線程;
  • 注意事項(xiàng):該方法只能調(diào)用一次,再次調(diào)用不僅無法讓線程再次執(zhí)行,還會(huì)拋出非法線程狀態(tài)異常;

② run()

  • 作用:run() 方法中放的是任務(wù)的具體邏輯,該方法由 JVM 調(diào)用,一般情況下開發(fā)者不需要直接調(diào)用該方法;
  • 注意事項(xiàng):如果你調(diào)用了 run() 方法,加上 JVM 也調(diào)用了一次,那這個(gè)方法就會(huì)執(zhí)行兩次

③ join()

  • 作用:join() 方法用于等待其他線程執(zhí)行結(jié)束;如果線程 A 調(diào)用了線程 B 的 join() 方法,那線程 A 會(huì)進(jìn)入等待狀態(tài),直到線程 B 運(yùn)行結(jié)束;
  • 注意事項(xiàng):join() 方法導(dǎo)致的等待狀態(tài)是可以被中斷的,所以調(diào)用這個(gè)方法需要捕獲中斷異常

④Thread.currentThread()

  • 作用:currentThread() 方法是一個(gè)靜態(tài)方法,用于獲取執(zhí)行當(dāng)前方法的線程;
  • 我們可以在任意方法中調(diào)用 Thread.currentThread() 獲取當(dāng)前線程,并設(shè)置它的名字和優(yōu)先級(jí)等屬性;

⑤Thread.yield()

  • 作用:yield() 方法是一個(gè)靜態(tài)方法,用于使當(dāng)前線程放棄對(duì)處理器的占用,相當(dāng)于是降低線程優(yōu)先級(jí);
  • 調(diào)用該方法就像是是對(duì)線程調(diào)度器說:“如果其他線程要處理器資源,那就給它們,否則我繼續(xù)用”;
  • 注意事項(xiàng):該方法不一定會(huì)讓線程進(jìn)入暫停狀態(tài);

⑥ Thread.sleep(ms)

作用:sleep(ms) 方法是一個(gè)靜態(tài)方法,用于使當(dāng)前線程在指定時(shí)間內(nèi)休眠(暫停)。

4、線程的六種狀態(tài)

①線程的生命周期

線程的生命周期不僅可以由開發(fā)者觸發(fā),還會(huì)受到其他線程的影響,下面是線程各個(gè)狀態(tài)之間的轉(zhuǎn)換示意圖;

我們可以通過 Thread.getState() 獲取線程的狀態(tài),該方法返回的是一個(gè)枚舉類 Thread.State;

線程的狀態(tài)有新建、可運(yùn)行、阻塞、等待、限時(shí)等待和終止 6 種,下面我們就來看看這 6 種狀態(tài)之間的轉(zhuǎn)換過程;

新建狀態(tài):當(dāng)一個(gè)線程創(chuàng)建后未啟動(dòng)時(shí),它就處于新建(NEW)狀態(tài);

②可運(yùn)行狀態(tài):當(dāng)我們調(diào)用線程的 start() 方法后,線程就進(jìn)入了可運(yùn)行(RUNNABLE)狀態(tài),可運(yùn)行狀態(tài)又分為預(yù)備(READY)和運(yùn)行(RUNNING)狀態(tài);

③預(yù)備狀態(tài):處于預(yù)備狀態(tài)的線程可被線程調(diào)度器調(diào)度,調(diào)度后線程的狀態(tài)會(huì)從預(yù)備轉(zhuǎn)換為運(yùn)行狀態(tài),處于預(yù)備狀態(tài)的線程也叫活躍線程,當(dāng)線程的 yield() 方法被調(diào)用后,線程的狀態(tài)可能由運(yùn)行狀態(tài)變?yōu)轭A(yù)備狀態(tài)

④運(yùn)行狀態(tài):運(yùn)行狀態(tài)表示線程正在運(yùn)行,也就是處理器正在執(zhí)行線程的 run() 方法;

⑤阻塞狀態(tài):當(dāng)下面幾種情況發(fā)生時(shí),線程就處于阻塞(BLOCKED)狀態(tài),發(fā)起阻塞式 I/O 操作、申請(qǐng)其他線程持有的鎖、進(jìn)入一個(gè) synchronized 方法或代碼塊失敗;

⑥等待狀態(tài):一個(gè)線程執(zhí)行特定方法后,會(huì)等待其他線程執(zhí)行執(zhí)行完畢,此時(shí)線程進(jìn)入了等待(WAITING)狀態(tài);

⑦等待狀態(tài):下面的幾個(gè)方法可以讓線程進(jìn)入等待狀態(tài);

  • Object.wait()
  • LockSupport.park()
  • Thread.join()

可運(yùn)行狀態(tài):下面的幾個(gè)方法可以讓線程從等待狀態(tài)轉(zhuǎn)變?yōu)榭蛇\(yùn)行狀態(tài),而這種轉(zhuǎn)變又叫喚醒;

  • Object.notify()
  • Object.notifyAll()
  • LockSupport.unpark()

⑧限時(shí)等待狀態(tài)

  • 限時(shí)等待狀態(tài) (TIMED_WAITING)與等待狀態(tài)的區(qū)別就是,限時(shí)等待是等待一段時(shí)間,時(shí)間到了之后就會(huì)轉(zhuǎn)換為可運(yùn)行狀態(tài);
  • 下面的幾個(gè)方法可以讓線程進(jìn)入限時(shí)等待狀態(tài),下面的方法中的 ms、ns、time 參數(shù)分別代表毫秒、納秒以及絕對(duì)時(shí)間;
  • Thread.sleep(ms);
  • Thread.join(ms);
  • Object.wait(ms);
  • LockSupport.parkNonos(ns);
  • LockSupport.parkUntil(time);

⑨ 終止?fàn)顟B(tài)

當(dāng)線程的任務(wù)執(zhí)行完畢或者任務(wù)執(zhí)行遇到異常時(shí),線程就處于終止(TERMINATED)狀態(tài);

二、線程調(diào)度的原理

線程調(diào)度原理相關(guān)的對(duì) Java 內(nèi)存模型、高速緩存、Java 線程調(diào)度機(jī)制進(jìn)行一個(gè)簡單介紹;

1、Java 的內(nèi)存模型

  • Java 內(nèi)存模型規(guī)定了所有變量都存儲(chǔ)在主內(nèi)存中,每條線程都有自己的工作內(nèi)存;
  • JVM 把內(nèi)存劃分成了好幾塊,其中方法區(qū)和堆內(nèi)存區(qū)域是線程共享的;
  • java內(nèi)存模型詳解

2、高速緩存

①高速緩存簡介

現(xiàn)代處理器的處理能力要遠(yuǎn)勝于主內(nèi)存(DRAM)的訪問速率,主內(nèi)存執(zhí)行一次內(nèi)存讀/寫操作需要的時(shí)間,如果給處理器使用,處理器可以執(zhí)行上百條指令;

為了彌補(bǔ)處理器與主內(nèi)存之間的差距,硬件設(shè)計(jì)者在主內(nèi)存與處理器之間加入了高速緩存(Cache);

處理器執(zhí)行內(nèi)存讀寫操作時(shí),不是直接與主內(nèi)存打交道,而是通過高速緩存進(jìn)行的;

高速緩存相當(dāng)于是一個(gè)由硬件實(shí)現(xiàn)的容量極小的散列表,這個(gè)散列表的 key 是一個(gè)對(duì)象的內(nèi)存地址,value 可以是內(nèi)存數(shù)據(jù)的副本,也可以是準(zhǔn)備寫入內(nèi)存的數(shù)據(jù);

②高速緩存內(nèi)部結(jié)構(gòu)

從內(nèi)部結(jié)構(gòu)來看,高速緩存相當(dāng)于是一個(gè)鏈?zhǔn)缴⒘斜?Chained Hash Table),它包含若干個(gè)桶,每個(gè)桶包含若干個(gè)緩存條目(Cache Entry);

③緩存條目結(jié)構(gòu)

緩存條目可進(jìn)一步劃分為 Tag、Data Block 和 Flag 三個(gè)部分;

Tag:包含了與緩存行中數(shù)據(jù)對(duì)應(yīng)的內(nèi)存地址的部分信息(內(nèi)存地址的高位部分比特)

Data: Block 也叫緩存行(Cache Line),是高速緩存與主內(nèi)存之間數(shù)據(jù)交換的最小單元,可以存儲(chǔ)從內(nèi)存中讀取的數(shù)據(jù),也可以存儲(chǔ)準(zhǔn)備寫進(jìn)內(nèi)存的數(shù)據(jù);

Flag: 用于表示對(duì)應(yīng)緩存行的狀態(tài)信息

3、Java 線程調(diào)度機(jī)制

在任意時(shí)刻,CPU 只能執(zhí)行一條機(jī)器指令,每個(gè)線程只有獲取到 CPU 的使用權(quán)后,才可以執(zhí)行指令;

也就是在任意時(shí)刻,只有一個(gè)線程占用 CPU,處于運(yùn)行的狀態(tài);

多線程并發(fā)運(yùn)行實(shí)際上是指多個(gè)線程輪流獲取 CPU 使用權(quán),分別執(zhí)行各自的任務(wù);

線程的調(diào)度由 JVM 負(fù)責(zé),線程的調(diào)度是按照特定的機(jī)制為多個(gè)線程分配 CPU 的使用權(quán);

線程調(diào)度模型分為兩類:分時(shí)調(diào)度模型和搶占式調(diào)度模型;

①分時(shí)調(diào)度模型

分時(shí)調(diào)度模型是讓所有線程輪流獲取 CPU 使用權(quán),并且平均分配每個(gè)線程占用 CPU 的時(shí)間片;

②搶占式調(diào)度模型

  • JVM 采用的是搶占式調(diào)度模型,也就是先讓優(yōu)先級(jí)高的線程占用 CPU,如果線程的優(yōu)先級(jí)都一樣,那就隨機(jī)選擇一個(gè)線程,并讓該線程占用 CPU;
  • 也就是如果我們同時(shí)啟動(dòng)多個(gè)線程,并不能保證它們能輪流獲取到均等的時(shí)間片;
  • 如果我們的程序想干預(yù)線程的調(diào)度過程,最簡單的辦法就是給每個(gè)線程設(shè)定一個(gè)優(yōu)先級(jí);

三、線程的安全性問題詳解

線程安全問題不是說線程不安全,而是多個(gè)線程之間交錯(cuò)操作有可能導(dǎo)致數(shù)據(jù)異常;

下面我們就來看下與線程安全相關(guān)的競態(tài)和實(shí)現(xiàn)線程安全要保證的三個(gè)點(diǎn):原子性、可見性和有序性;

①原子性

  • 原子(Atomic)的字面意識(shí)是不可分割的,對(duì)于涉及共享變量訪問的操作,若該操作從其執(zhí)行線程以外的任意線程看來是不可分割的,那么該操作就是原子操作,相應(yīng)地稱該操作具有原子性(Atomicity);
  • 所謂不可分割,就是訪問(讀/寫)某個(gè)共享變量的操作,從執(zhí)行線程以外的其他線程看來,該操作只有未開始和結(jié)束兩種狀態(tài),不會(huì)知道該操作的中間部分;
  • 訪問同一組共享變量的原子操作是不能被交錯(cuò)的,這就排除了一個(gè)線程執(zhí)行一個(gè)操作的期間,另一個(gè)線程讀取或更新該操作鎖訪問的共享變量,導(dǎo)致臟數(shù)據(jù)和丟失更新;

②可見性

  • 在多線程環(huán)境下,一個(gè)線程對(duì)某個(gè)共享變量進(jìn)行更新后,后續(xù)訪問該變量的線程可能無法立刻讀取到這個(gè)更新的結(jié)果,甚至永遠(yuǎn)也無法讀取到這個(gè)更新的結(jié)果,這就是線程安全問題的另一種表現(xiàn)形式:可見性;
  • 可見性是指一個(gè)線程對(duì)共享變量的更新,對(duì)于其他讀取該變量的線程是否可見;
  • 可見性問題與計(jì)算機(jī)的存儲(chǔ)系統(tǒng)有關(guān),程序中的變量可能會(huì)被分配到寄存器而不是主內(nèi)存中,每個(gè)處理器都有自己的寄存器,一個(gè)處理器無法讀取另一個(gè)處理器的寄存器上的內(nèi)容;
  • 即使共享變量是分配到主內(nèi)存中存儲(chǔ)的,也不餓能保證可見性,因?yàn)樘幚砥鞑皇侵苯釉L問主內(nèi)存,而是通過高速緩存(Cache)進(jìn)行的;
  • 一個(gè)處理器上運(yùn)行的線程對(duì)變量的更新,可能只是更新到該處理器的寫緩沖器(Store Buffer)中,還沒有到高速緩存中,更別說處理器了;
  • 可見性描述的是一個(gè)線程對(duì)共享變量的更新,對(duì)于另一個(gè)線程是否可見,保證可見性意味著一個(gè)線程可以讀取到對(duì)應(yīng)共享變量的新值;
  • 從保證線程安全的角度來看,光保證原子性還不夠,還要保證可見性,同時(shí)保證可見性和原子性才能確保一個(gè)線程能正確地看到其他線程對(duì)共享變量做的更新;

③ 有序性

  • 有序性是指一個(gè)處理器在為一個(gè)線程執(zhí)行的內(nèi)存訪問操作,對(duì)于另一個(gè)處理器上運(yùn)行的線程來看是亂序的;
  • 順序結(jié)構(gòu)是結(jié)構(gòu)化編程中的一種基本結(jié)構(gòu),它表示我們希望某個(gè)操作先于另外一個(gè)操作執(zhí)行;
  • 但是在多核處理器的環(huán)境下,代碼的執(zhí)行順序是沒保障的,編譯器可能改變兩個(gè)操作的先后順序,處理器也可能不是按照程序代碼的順序執(zhí)行指令;
  • 重排序(Reordering)處理器和編譯器是對(duì)代碼做的一種優(yōu)化,它可以在不影響單線程程序正確性的情況下提升程序的性能,但是它會(huì)對(duì)多線程程序的正確性產(chǎn)生影響,導(dǎo)致線程安全問題;
  • 現(xiàn)代處理器為了提高指令的執(zhí)行效率,往往不是按程序順序注意執(zhí)行指令的,而是哪條指令就緒就先執(zhí)行哪條指令,這就是處理器的亂序執(zhí)行;

四、實(shí)現(xiàn)線程安全

要實(shí)現(xiàn)線程安全就要保證上面說到的原子性、可見性和有序性;

常見的實(shí)現(xiàn)線程安全的辦法是使用鎖和原子類型,而鎖可分為內(nèi)部鎖、顯式鎖、讀寫鎖、輕量級(jí)鎖(volatile)四種;

下面我們就來看看這四種鎖和原子類型的用法和特點(diǎn);

1、鎖

是鎖(Lock)的作用,讓多個(gè)線程更好地協(xié)作,避免多個(gè)線程的操作交錯(cuò)導(dǎo)致數(shù)據(jù)異常的問題;

鎖的五個(gè)特點(diǎn):

  • 臨界區(qū):持有鎖的線程獲得鎖后和釋放鎖前執(zhí)行的代碼叫做臨界區(qū)(Critical Section);
  • 排他性:鎖具有排他性,能夠保障一個(gè)共享變量在任一時(shí)刻只能被一個(gè)線程訪問,這就保證了臨界區(qū)代碼一次只能夠被一個(gè)線程執(zhí)行,臨界區(qū)的操作具有不可分割性,也就保證了原子性;
  • 串行:鎖相當(dāng)于是把多個(gè)線程對(duì)共享變量的操作從并發(fā)改為串行;
  • 三種保障:鎖能夠保護(hù)共享變量實(shí)現(xiàn)線程安全,它的作用包括保障原子性、可見性和有序性;
  • 調(diào)度策略:鎖的調(diào)度策略分為公平策略和非公平策略,對(duì)應(yīng)的鎖就叫 公平鎖和非公平鎖;公平鎖會(huì)在加鎖前查看是否有排隊(duì)等待的線程,有的話會(huì)優(yōu)先處理排在前面的線程;公平鎖以增加上下文切換為代價(jià),保障了鎖調(diào)度的公平性,增加了線程暫停和喚醒的可能性;
  • 公平鎖的開銷比非公平鎖大,所以 ReentrantLock 的默認(rèn)調(diào)度策略是非公平策略;

2、 volatile 關(guān)鍵字

volatile 關(guān)鍵字可用于修飾共享變量,對(duì)應(yīng)的變量就叫 volatile 變量,volatile 變量有下面幾個(gè)特點(diǎn);

  • 易變化:volatile 的字面意思是“不穩(wěn)定的”,也就是 volatile 用于修飾容易發(fā)生變化的變量,不穩(wěn)定指的是對(duì)這種變量的讀寫操作要從高速緩存或主內(nèi)存中讀取,而不會(huì)分配到寄存器中;
  • 比鎖低:volatile 的開銷比鎖低,volatile 變量的讀寫操作不會(huì)導(dǎo)致上下文切換,所以 volatile 關(guān)鍵字也叫輕量級(jí)鎖 ;
  • 比普通變量高:volatile 變量讀操作的開銷比普通變量要高,這是因?yàn)?volatile 變量的值每次都要從高速緩存或主內(nèi)存中讀取,無法被暫存到寄存器中;
  • 釋放/存儲(chǔ)屏障:對(duì)于 volatile 變量的寫操作,JVM 會(huì)在該操作前插入一個(gè)釋放屏障,并在該操作后插入一個(gè)存儲(chǔ)屏障;存儲(chǔ)屏障具有沖刷處理器緩存的作用,所以在 volatile 變量寫操作后插入一個(gè)存儲(chǔ)屏障,能讓該存儲(chǔ)屏障前的所有操作結(jié)果對(duì)其他處理器來說是同步的;
  • 加載/獲取屏障:對(duì)于 volatile 變量的讀操作,JVM 會(huì)在該操作前插入一個(gè)加載屏障,并在操作后插入一個(gè)獲取屏障;加載屏障通過沖刷處理器緩存,使線程所在的處理器將其他處理器對(duì)該共享變量做的更新同步到該處理器的高速緩存中;
  • 保證有序性:volatile 能禁止指令重排序,也就是使用 volatile 能保證操作的有序性;
  • 保證可見性:讀線程執(zhí)行的加載屏障和寫線程執(zhí)行的存儲(chǔ)屏障配合在一起,能讓寫線程對(duì) volatile 變量的寫操作對(duì)讀線程可見,從而保證了可見性;
  • 原子性:在原子性方面,對(duì)于 long/double 型變量,volatile 能保證讀寫操作的原子型;對(duì)于非 long/double 型變量,volatile 只能保證寫操作的原子性;如果 volatile 變量寫操作前涉及共享變量,競態(tài)仍然可能發(fā)生,因?yàn)楣蚕碜兞抠x值給 volatile 變量時(shí),其他線程可能已經(jīng)更新了該共享變量的值;

3、原子類型

原子類型簡介:

在 JUC 下有一個(gè) atomic 包,這個(gè)包里面有一組原子類,使用原子類的方法,不需要加鎖也能保證線程安全,而原子類是通過 Unsafe 類中的 CAS 指令從硬件層面來實(shí)現(xiàn)線程安全的;

這個(gè)包里面有如 AtomicInteger、AtomicBoolean、AtomicReference、AtomicReferenceFIeldUpdater 等;

我們先來看一個(gè)使用原子整型 AtomicInteger 自增的例子;

// 初始值為 1

AtomicInteger integer = new AtomicInteger(1);

// 自增

int result = integer.incrementAndGet();

// 結(jié)果為 2

System.out.println(result);

AtomicReference 和 AtomicReferenceFIeldUpdater 可以讓我們自己的類具有原子性,它們的原理都是通過 Unsafe 的 CAS 操作實(shí)現(xiàn)的;

我們下面看下它們的用法和區(qū)別;

①、AtomicReference 基本用法

  1. class AtomicReferenceValueHolder { 
  2.   AtomicReference<String> atomicValue = new AtomicReference<>("HelloAtomic"); 
  3. public void getAndUpdateFromReference() { 
  4.   AtomicReferenceValueHolder holder = new AtomicReferenceValueHolder(); 
  5.   // 對(duì)比并設(shè)值 
  6.   // 如果值是 HelloAtomic,就把值換成 World 
  7.   holder.atomicValue.compareAndSet("HelloAtomic""World"); 
  8.   // World 
  9.   System.out.println(holder.atomicValue.get()); 
  10.   // 修改并獲取修改后的值 
  11.   String value = holder.atomicValue.updateAndGet(new UnaryOperator<String>() { 
  12.     @Override 
  13.     public String apply(String s) { 
  14.       return "HelloWorld"
  15.     } 
  16.   }); 
  17.   // Hello World   
  18.   System.out.println(value); 

② AtomicReferenceFieldUpdater 基本用法

AtomicReferenceFieldUpdater 在用法上和 AtomicReference 有些不同,我們直接把 String 值暴露了出來,并且用 volatile 對(duì)這個(gè)值進(jìn)行了修飾;

并且將當(dāng)前類和值的類傳到 newUpdater ()方法中獲取 Updater,這種用法有點(diǎn)像反射,而且 AtomicReferenceFieldUpdater 通常是作為類的靜態(tài)成員使用;

  1. public class SimpleValueHolder { 
  2.   public static AtomicReferenceFieldUpdater<SimpleValueHolder, String> valueUpdater 
  3.     = AtomicReferenceFieldUpdater.newUpdater( 
  4.       SimpleValueHolder.class, String.class, "value"); 
  5.   volatile String value = "HelloAtomic"
  6. public void getAndUpdateFromUpdater() { 
  7.   SimpleValueHolder holder = new SimpleValueHolder(); 
  8.   holder.valueUpdater.compareAndSet(holder, "HelloAtomic""World"); 
  9.   // World 
  10.   System.out.println(holder.valueUpdater.get(holder)); 
  11.   String value = holder.valueUpdater.updateAndGet(holder, new UnaryOperator<String>() { 
  12.     @Override 
  13.     public String apply(String s) { 
  14.       return "HelloWorld"
  15.     } 
  16.   }); 
  17.   // HelloWorld 
  18.   System.out.println(value); 

③AtomicReference 與 AtomicReferenceFieldUpdater 的區(qū)別

AtomicReference 和 AtomicReferenceFieldUpdater 的作用是差不多的,在用法上 AtomicReference 比 AtomicReferenceFIeldUpdater 更簡單;

但是在內(nèi)部實(shí)現(xiàn)上,AtomicReference 內(nèi)部一樣是有一個(gè) volatile 變量;

使用 AtomicReference 和使用 AtomicReferenceFIeldUpdater 比起來,要多創(chuàng)建一個(gè)對(duì)象;

對(duì)于 32 位的機(jī)器,這個(gè)對(duì)象的頭占 12 個(gè)字節(jié),它的成員占 4 個(gè)字節(jié),也就是多出來 16 個(gè)字節(jié);

對(duì)于 64 位的機(jī)器,如果啟動(dòng)了指針壓縮,那這個(gè)對(duì)象占用的也是 16 個(gè)字節(jié);

對(duì)于 64 位的機(jī)器,如果沒啟動(dòng)指針壓縮,那么這個(gè)對(duì)象就會(huì)占 24 個(gè)字節(jié),其中對(duì)象頭占 16 個(gè)字節(jié),成員占 8 個(gè)字節(jié);

當(dāng)要使用 AtomicReference 創(chuàng)建成千上萬個(gè)對(duì)象時(shí),這個(gè)開銷就會(huì)變得很大;

這也就是為什么 BufferedInputStream 、Kotlin 協(xié)程 和 Kotlin 的 lazy 的實(shí)現(xiàn)會(huì)選擇 AtomicReferenceFieldUpdater 作為原子類型;

因?yàn)殚_銷的原因,所以一般只有在原子類型創(chuàng)建的實(shí)例確定了較少的情況下,比如說是單例,才會(huì)選擇 AtomicReference,否則都是用 AtomicReferenceFieldUpdater;

4、 鎖的使用技巧

  • 使用鎖會(huì)帶來一定的開銷,而掌握鎖的使用技巧可以在一定程度上減少鎖帶來的開銷和潛在的問題,下面就是一些鎖的使用技巧;
  • 長鎖不如短鎖:盡量只對(duì)必要的部分加鎖;
  • 大鎖不如小鎖:進(jìn)可能對(duì)加鎖的對(duì)象拆分;
  • 公鎖不如私鎖:進(jìn)可能把鎖的邏輯放到私有代碼中,如果讓外部調(diào)用者加鎖,可能會(huì)導(dǎo)致鎖不正當(dāng)使用導(dǎo)致死鎖;
  • 嵌套鎖不如扁平鎖:在寫代碼時(shí)要避免鎖嵌套;
  • 分離讀寫鎖:盡可能將讀鎖和寫鎖分離;
  • 粗化高頻鎖:合并處理頻繁而且過短的鎖,因?yàn)槊恳话焰i都會(huì)帶來一定的開銷;
  • 消除無用鎖:盡可能不加鎖,或者用 volatile 代替;

五、線程的四個(gè)活躍性問題

1、死鎖

死鎖是線程的一種常見多線程活躍性問題,如果兩個(gè)或更多的線程,因?yàn)橄嗷サ却龑?duì)方而被永遠(yuǎn)暫停,那么這就叫死鎖現(xiàn)象;

下面我們就來看看死鎖產(chǎn)生的四個(gè)條件和避免死鎖的三個(gè)方法;

2、死鎖產(chǎn)生的四個(gè)條件

當(dāng)多個(gè)線程發(fā)生了死鎖后,這些線程和相關(guān)共享變量就會(huì)滿足下面四個(gè)條件:

  • 資源互斥:涉及的資源必須是獨(dú)占的,也就是資源每次只能被一個(gè)線程使用
  • 資源不可搶奪:涉及的資源只能被持有該資源的線程主動(dòng)釋放,無法被其他線程搶奪(被動(dòng)釋放)
  • 占用并等待資源:涉及的線程至少持有一個(gè)資源,還申請(qǐng)了其他資源,而其他資源剛好被其他線程持有,并且線程不釋放已持有資源
  • 循環(huán)等待資源:涉及的線程必須等待別的線程持有的資源,而別的線程又反過來等待該線程持有的資源

只要產(chǎn)生了死鎖,上面的條件就一定成立,但是上面的條件都成立也不一定會(huì)產(chǎn)生死鎖;

3、 避免死鎖的三個(gè)方法

要想消除死鎖,只要破壞掉上面的其中一個(gè)條件即可;

由于鎖具有排他性,且無法被動(dòng)釋放,所以我們只能破壞掉第三個(gè)和第四個(gè)條件;

①、粗鎖法

  • 使用粗粒度的鎖代替多個(gè)鎖,鎖的范圍變大了,訪問共享資源的多個(gè)線程都只需要申請(qǐng)一個(gè)鎖,因?yàn)槊總€(gè)線程只需要申請(qǐng)一個(gè)鎖就可以執(zhí)行自己的任務(wù),這樣“占用并等待資源”和“循環(huán)等待資源”這兩個(gè)條件就不成立了;
  • 粗鎖法的缺點(diǎn)是會(huì)降低并發(fā)性,而且可能導(dǎo)致資源浪費(fèi),因?yàn)椴捎么宙i法時(shí),一次只能有一個(gè)線程訪問資源,這樣其他線程就只能擱置任務(wù)了;

②鎖排序法

鎖排序法指的是相關(guān)線程使用全局統(tǒng)一的順序申請(qǐng)鎖;

假如有多個(gè)線程需要申請(qǐng)鎖,我們只需要讓這些線程按照一個(gè)全局統(tǒng)一的順序去申請(qǐng)鎖,這樣就能破壞“循環(huán)等待資源”這個(gè)條件;

③tryLock

顯式鎖 ReentrantLock.tryLock(long timeUnit) 這個(gè)方法允許我們?yōu)樯暾?qǐng)鎖的操作設(shè)置超時(shí)時(shí)間,這樣就能破壞“占用并等待資源”這個(gè)條件;

④開放調(diào)用

開放調(diào)用(Open Call)就是一個(gè)方法在調(diào)用外部方法時(shí)不持有鎖,開放調(diào)用能破壞“占用并等待資源”這個(gè)條件;

六、線程之間怎么協(xié)作?

線程間的常見協(xié)作方式有兩種:等待和中斷;

當(dāng)一個(gè)線程中的操作需要等待另一個(gè)線程中的操作結(jié)束時(shí),就涉及到等待型線程協(xié)作方式;

常用的等待型線程協(xié)作方式有 join、wait/notify、await/signal、await/countDown 和 CyclicBarrier 五種,下面我們就來看看這五種線程協(xié)作方式的用法和區(qū)別;

1、join

  • 使用 Thread.join() 方法,我們可以讓一個(gè)線程等待另一個(gè)線程執(zhí)行結(jié)束后再繼續(xù)執(zhí)行;
  • join() 方法實(shí)現(xiàn)等待是通過 wait() 方法實(shí)現(xiàn)的,在 join() 方法中,會(huì)不斷判斷調(diào)用了 join() 方法的線程是否還存活,是的話則繼續(xù)等待;

下面是 join() 方法的簡單用法;

  1. public void tryJoin() { 
  2.   Thread threadA = new ThreadA(); 
  3.   Thread threadB = new ThreadB(threadA); 
  4.   threadA.start(); 
  5.   threadB.start(); 
  6. public class ThreadA extends Thread { 
  7.   @Override 
  8.   public void run() { 
  9.     System.out.println("線程 A 開始執(zhí)行"); 
  10.     ThreadUtils.sleep(1000); 
  11.     System.out.println("線程 A 執(zhí)行結(jié)束"); 
  12.   } 
  13. public class ThreadB extends Thread { 
  14.   private final Thread threadA; 
  15.   public ThreadB(Thread thread) { 
  16.     threadA = thread; 
  17.   } 
  18.   @Override 
  19.   public void run() { 
  20.     try { 
  21.       System.out.println("線程 B 開始等待線程 A 執(zhí)行結(jié)束"); 
  22.       threadA.join(); 
  23.       System.out.println("線程 B 結(jié)束等待,開始做自己想做的事情"); 
  24.     } catch (InterruptedException e) { 
  25.       e.printStackTrace(); 
  26.     } 
  27.   } 

2、 wait/notify

  • 一個(gè)線程因?yàn)閳?zhí)行操作(目標(biāo)動(dòng)作)所需的保護(hù)條件未滿足而被暫停的過程就叫等待(wait);
  • 一個(gè)線程更新了共享變量,使得其他線程需要的保護(hù)條件成立,喚醒了被暫停的線程的過程就叫通知(notify);
  • wait() 方法的執(zhí)行線程叫等待線程,notify() 方法執(zhí)行的線程叫通知線程;

下面是 wait/notify 使用的示例代碼;

  1. final Object lock = new Object(); 
  2. private volatile boolean conditionSatisfied; 
  3. public void startWait() throws InterruptedException { 
  4.   synchronized (lock) { 
  5.     System.out.println("等待線程獲取了鎖"); 
  6.     while(!conditionSatisfied) { 
  7.       System.out.println("保護(hù)條件不成立,等待線程進(jìn)入等待狀態(tài)"); 
  8.       lock.wait(); 
  9.     } 
  10.     System.out.println("等待線程被喚醒,開始執(zhí)行目標(biāo)動(dòng)作"); 
  11.   } 
  12. public void startNotify() { 
  13.   synchronized (lock) { 
  14.     System.out.println("通知線程獲取了鎖"); 
  15.     System.out.println("通知線程即將喚醒等待線程"); 
  16.     conditionSatisfied = true
  17.     lock.notify(); 
  18.   } 

3、 wait/notify 原理

  • JVM 會(huì)給每個(gè)對(duì)象維護(hù)一個(gè)入口集(Entry Set)和等待集(Wait Set);
  • 入口集用于存儲(chǔ)申請(qǐng)?jiān)搶?duì)象內(nèi)部鎖的線程,等待集用于存儲(chǔ)對(duì)象上的等待線程;
  • wait() 方法會(huì)將當(dāng)前線程暫停,在釋放內(nèi)部鎖時(shí),會(huì)將當(dāng)前線程存入該方法所屬的對(duì)象等待集中;
  • 調(diào)用對(duì)象的 notify() 方法,會(huì)讓該對(duì)象的等待集中的任意一個(gè)線程喚醒,被喚醒的線程會(huì)繼續(xù)留在對(duì)象的等待集中,直到該線程再次持有對(duì)應(yīng)的內(nèi)部鎖時(shí),wait() 方法就會(huì)把當(dāng)前線程從對(duì)象的等待集中移除;
  • 添加當(dāng)前線程到等待集、暫停當(dāng)前線程、釋放鎖以及把喚醒后的線程從對(duì)象的等待集中移除,都是在 wait() 方法中實(shí)現(xiàn)的;
  • 在 wait() 方法的 native 代碼中,會(huì)判斷線程是否持有當(dāng)前對(duì)象的內(nèi)部鎖,如果沒有的話,就會(huì)報(bào)非法監(jiān)視器狀態(tài)異常,這也就是為什么要在同步代碼塊中執(zhí)行 wait() 方法;

4、notify()/notifyAll()

notify() 可能導(dǎo)致信號(hào)丟失,而 notifyAll() 雖然會(huì)把不需要喚醒的等待線程也喚醒,但是在正確性方面有保障;

所以一般情況下優(yōu)先使用 notifyAll() 保障正確性;

一般情況下,只有在下面兩個(gè)條件都實(shí)現(xiàn)時(shí),才會(huì)選擇使用 notify() 實(shí)現(xiàn)通知;

①只需喚醒一個(gè)線程

當(dāng)一次通知只需要喚醒最多一個(gè)線程時(shí),我們可以考慮使用 notify() 實(shí)現(xiàn)通知,但是光滿足這個(gè)條件還不夠;

在不同的等待線程使用不同的保護(hù)條件時(shí),notify() 喚醒的一個(gè)任意線程可能不是我們需要喚醒的那個(gè)線程,所以需要條件 2 來排除;

②對(duì)象的等待集中只包含同質(zhì)等待線程

同質(zhì)等待線程指的是線程使用同一個(gè)保護(hù)條件并且 wait() 調(diào)用返回后的邏輯一致;

最典型的同質(zhì)線程是使用同一個(gè) Runnable 創(chuàng)建的不同線程,或者同一個(gè) Thread 子類 new 出來的多個(gè)實(shí)例;

5、await/signal

wait()/notify() 過于底層,而且還存在兩個(gè)問題,一是過早喚醒、二是無法區(qū)分 Object.wait(ms) 返回是由于等待超時(shí)還是被通知線程喚醒;

await/signal 基本用法

  1. private Lock lock = new ReentrantLock(); 
  2. private Condition condition = lock.newCondition(); 
  3. private volatile boolean conditionSatisfied = false
  4. private void startWait() { 
  5.   lock.lock(); 
  6.   System.out.println("等待線程獲取了鎖"); 
  7.   try { 
  8.     while (!conditionSatisfied) { 
  9.       System.out.println("保護(hù)條件不成立,等待線程進(jìn)入等待狀態(tài)"); 
  10.       condition.await(); 
  11.     } 
  12.     System.out.println("等待線程被喚醒,開始執(zhí)行目標(biāo)動(dòng)作"); 
  13.   } catch (InterruptedException e) { 
  14.     e.printStackTrace(); 
  15.   } finally { 
  16.     lock.unlock(); 
  17.     System.out.println("等待線程釋放了鎖"); 
  18.   } 
  19. public void startNotify() { 
  20.   lock.lock(); 
  21.   System.out.println("通知線程獲取了鎖"); 
  22.   try { 
  23.     conditionSatisfied = true
  24.     System.out.println("通知線程即將喚醒等待線程"); 
  25.     condition.signal(); 
  26.   } finally { 
  27.     System.out.println("通知線程釋放了鎖"); 
  28.     lock.unlock(); 
  29.   } 
  • 當(dāng)我們?cè)趦蓚€(gè)線程中分別執(zhí)行了上面的兩個(gè)函數(shù)后,能得到下面的輸出;
  • 等待線程獲取了鎖
  • 保護(hù)條件不成立,等待線程進(jìn)入等待狀態(tài)
  • 通知線程獲取了鎖
  • 通知線程即將喚醒等待線程
  • 等待線程被喚醒,開始執(zhí)行目標(biāo)動(dòng)作

6、 awaitUntil() 用法

awaitUntil(timeout, unit) 方法;

如果是由于超時(shí)導(dǎo)致等待結(jié)束,那么 awaitUntil() 會(huì)返回 false,否則會(huì)返回 true,表示等待是被喚醒的,下面我們就看看這個(gè)方法是怎么用的;

  1. private void startTimedWait() throws InterruptedException { 
  2.   lock.lock(); 
  3.   System.out.println("等待線程獲取了鎖"); 
  4.   // 3 秒后超時(shí) 
  5.   Date date = new Date(System.currentTimeMillis() + 3 * 1000); 
  6.   boolean isWakenUp = true
  7.   try { 
  8.     while (!conditionSatisfied) { 
  9.       if (!isWakenUp) { 
  10.         System.out.println("已超時(shí),結(jié)束等待任務(wù)"); 
  11.         return
  12.       } else { 
  13.         System.out.println("保護(hù)條件不滿足,并且等待時(shí)間未到,等待進(jìn)入等待狀態(tài)"); 
  14.         isWakenUp = condition.awaitUntil(date); 
  15.       } 
  16.     } 
  17.     System.out.println("等待線程被喚醒,開始執(zhí)行目標(biāo)動(dòng)作"); 
  18.   } finally { 
  19.       lock.unlock(); 
  20.   } 
  21. public void startDelayedNotify() { 
  22.   threadSleep(4 * 1000); 
  23.   startNotify(); 
  • 等待線程獲取了鎖
  • 保護(hù)條件不滿足,并且等待時(shí)間未到,等待進(jìn)入等待狀態(tài)
  • 已超時(shí),結(jié)束等待任務(wù)
  • 通知線程獲取了鎖
  • 通知線程即將喚醒等待線程

7、 await/countDown

使用 join() 實(shí)現(xiàn)的是一個(gè)線程等待另一個(gè)線程執(zhí)行結(jié)束,但是有的時(shí)候我們只是想要一個(gè)特定的操作執(zhí)行結(jié)束,不需要等待整個(gè)線程執(zhí)行結(jié)束,這時(shí)候就可以使用 CountDownLatch 來實(shí)現(xiàn);

await/countDown 基本用法

  1. public void tryAwaitCountDown() { 
  2.   startWaitThread(); 
  3.   startCountDownThread(); 
  4.   startCountDownThread(); 
  5. final int prerequisiteOperationCount = 2; 
  6. final CountDownLatch latch = new CountDownLatch(prerequisiteOperationCount); 
  7. private void startWait() throws InterruptedException { 
  8.   System.out.println("等待線程進(jìn)入等待狀態(tài)"); 
  9.   latch.await(); 
  10.   System.out.println("等待線程結(jié)束等待"); 
  11. private void startCountDown() { 
  12.   try { 
  13.     System.out.println("執(zhí)行先決操作"); 
  14.   } finally { 
  15.     System.out.println("計(jì)數(shù)值減 1"); 
  16.     latch.countDown(); 
  17.   } 

8、 CyclicBarrier

有的時(shí)候多個(gè)線程需要互相等待對(duì)方代碼中的某個(gè)地方(集合點(diǎn)),這些線程才能繼續(xù)執(zhí)行,這時(shí)可以使用 CyclicBarrier(柵欄);

CyclicBarrier 基本用法

  1. final int parties = 3; 
  2. final Runnable barrierAction = new Runnable() { 
  3.   @Override 
  4.   public void run() { 
  5.     System.out.println("人來齊了,開始爬山"); 
  6.   } 
  7. }; 
  8. final CyclicBarrier barrier = new CyclicBarrier(parties, barrierAction); 
  9. public void tryCyclicBarrier() { 
  10.   firstDayClimb(); 
  11.   secondDayClimb(); 
  12. private void firstDayClimb() { 
  13.   new PartyThread("第一天爬山,老李先來").start(); 
  14.   new PartyThread("老王到了,小張還沒到").start(); 
  15.   new PartyThread("小張到了").start(); 
  16. private void secondDayClimb() { 
  17.   new PartyThread("第二天爬山,老王先來").start(); 
  18.   new PartyThread("小張到了,老李還沒到").start(); 
  19.   new PartyThread("老李到了").start(); 
  20. public class PartyThread extends Thread { 
  21.   private final String content; 
  22.   public PartyThread(String content) { 
  23.     this.content = content; 
  24.   } 
  25.   @Override 
  26.   public void run() { 
  27.     System.out.println(content); 
  28.     try { 
  29.       barrier.await(); 
  30.     } catch (BrokenBarrierException e) { 
  31.       e.printStackTrace(); 
  32.     } catch (InterruptedException e) { 
  33.       e.printStackTrace(); 
  34.     } 
  35.   } 

Android 中常用的 7 種異步方式:Thread、HandlerThread、IntentService、AsyncTask、線程池、RxJava 和 Kotlin 協(xié)程;

總結(jié):

1、線程有很多優(yōu)勢(shì):

  • 提高多處理器的利用效率;
  • 簡化業(yè)務(wù)功能設(shè)計(jì);
  • 實(shí)現(xiàn)異步處理;

2、多線程的風(fēng)險(xiǎn):

  • 共享數(shù)據(jù)的線程安全性;
  • 多線程執(zhí)行時(shí)的活躍性問題;
  • 多線程的所帶來的性能損失問題;

3、下次詳解下Android中常用異步方式,從實(shí)際出發(fā)。

 

責(zé)任編輯:武曉燕 來源: Android開發(fā)編程
相關(guān)推薦

2021-09-11 07:32:15

Java線程線程池

2025-02-25 12:00:00

Java線程開發(fā)

2023-10-31 08:22:31

線程類型.NET

2015-09-08 15:13:35

Android進(jìn)程與線程

2009-12-08 10:07:29

2021-09-18 06:56:01

JavaCAS機(jī)制

2025-02-24 00:00:10

.NET線程池模型

2025-04-28 00:55:00

2025-05-09 10:14:19

2022-05-26 08:31:41

線程Java線程與進(jìn)程

2023-11-06 18:37:23

虛擬線程編寫

2021-09-01 06:48:16

AndroidGlide緩存

2023-12-29 09:38:00

Java線程池

2021-07-27 20:51:02

AndroidDNS網(wǎng)絡(luò)

2013-06-08 13:07:23

Java線程池調(diào)度器

2023-07-10 16:18:18

性能優(yōu)化開發(fā)

2021-07-29 14:20:34

網(wǎng)絡(luò)優(yōu)化移動(dòng)互聯(lián)網(wǎng)數(shù)據(jù)存儲(chǔ)

2024-12-02 00:57:17

非阻塞異步編程

2025-09-29 05:00:00

Linux線程棧內(nèi)存

2023-06-09 07:59:37

多線程編程鎖機(jī)制
點(diǎn)贊
收藏

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

亚洲波多野结衣| 亚洲欧美国产日韩综合| 日韩有码电影| 日韩影院免费视频| 蜜臀久久99精品久久久无需会员| 香蕉在线观看视频| 免费亚洲电影| 亚洲三级免费观看| 久久av二区| 一级欧美一级日韩| 一区二区久久| 久久精品夜夜夜夜夜久久| 麻豆精品国产传媒av| 999国产精品亚洲77777| 亚洲最新视频在线观看| 欧美日韩国产一二| 性生活黄色大片| 日本不卡视频在线观看| 国模精品系列视频| 国产精品久久免费观看| 国产96在线亚洲| 欧美精品日韩一区| 国产真实乱子伦| 日韩精品卡一| 国产精品久久久久影院| 久久超碰亚洲| 亚洲第一大网站| 久久99国产精品久久| 欧美中文字幕在线| 精品无码人妻一区二区三区| 91欧美在线| 亚洲午夜久久久影院| 99久久一区三区四区免费| 偷拍亚洲精品| 色婷婷综合久色| 成品人视频ww入口| 国产cdts系列另类在线观看| 国产女主播一区| 精品国产_亚洲人成在线| 国产区精品在线| 日韩**一区毛片| 欧美洲成人男女午夜视频| 欧美黄色免费看| 99免费精品| 最新的欧美黄色| 夜夜春很很躁夜夜躁| 美女毛片一区二区三区四区最新中文字幕亚洲 | 可以直接看的无码av| 午夜视频在线观看精品中文| 欧美高清视频一二三区| 午夜免费高清视频| 欧亚一区二区| 日本乱人伦一区| 国产精品免费成人| xxx欧美xxx| 日韩欧美在线看| 日本精品一区在线观看| 性感女国产在线| 欧美日韩精品中文字幕| 国产成人无码a区在线观看视频| 538在线视频| 五月激情综合婷婷| 国产91在线免费| 不卡福利视频| 在线观看日韩一区| 国产精品视频分类| 欧美午夜三级| 91麻豆精品91久久久久同性| 色综合久久久无码中文字幕波多| 欧州一区二区三区| 精品久久久久久久久久久久久久久久久| 日本r级电影在线观看| 国产色99精品9i| 欧美videossexotv100| 国产污在线观看| 伊甸园亚洲一区| 国产亚洲欧美日韩精品| 欧美性猛交xxxx乱大交少妇| 图片小说视频色综合| 欧美成人精品在线播放| 国产va在线播放| 亚洲精品婷婷| 国产成人精品久久二区二区91| 懂色av蜜臀av粉嫩av喷吹| 久久超碰97人人做人人爱| 成人在线小视频| 亚洲免费成人在线| 久久久国产一区二区三区四区小说 | 国产欧美日韩综合精品一区二区| 亚洲一区二区高清视频| 日韩av毛片| 色综合视频在线观看| 久久6免费视频| 精品资源在线| 最近日韩中文字幕中文| 麻豆视频在线观看| 鲁大师成人一区二区三区| 国产欧美日韩中文字幕| 国产香蕉在线观看| 国产欧美一区二区三区沐欲| 久久久天堂国产精品| 性欧美18~19sex高清播放| 欧美日韩免费高清一区色橹橹| 最新国产精品自拍| 精品精品99| 欧美国产视频日韩| jizz国产在线| www.日韩精品| 国产在线无码精品| 日韩欧美精品电影| 精品国产乱码久久久久久图片| 欧美黄色一级生活片| 欧美三区不卡| 国产裸体写真av一区二区| 欧美特黄一级视频| 一色桃子久久精品亚洲| 国产美女三级视频| 亚洲一级大片| 日韩视频在线观看免费| 日韩毛片一区二区三区| 国产精品一区专区| 亚洲国内在线| 美女100%一区| 亚洲第一精品夜夜躁人人爽| 久久国产高清视频| 日韩电影在线看| 美脚丝袜一区二区三区在线观看| av网址在线| 欧美日韩国产高清一区| 亚洲精品国产一区黑色丝袜| 亚洲第一黄网| 成人免费视频观看视频| 精品176二区| 欧美精品免费视频| 日本不卡一区视频| 天堂久久一区二区三区| 韩国一区二区三区美女美女秀| av毛片在线看| 91精品国产高清一区二区三区蜜臀| av网站免费在线看| 免费亚洲婷婷| 免费av在线一区二区| 成人黄色动漫| 亚洲国产精品一区二区久| 久久久久99精品成人片毛片| 国产精品一区在线观看你懂的| 亚洲天堂电影网| 一区在线影院| 在线成人中文字幕| 中文字幕日韩第一页| 国产日韩欧美综合一区| 欧美精品第三页| 欧美在线电影| 国产日韩欧美夫妻视频在线观看 | 久久久久97| 国a精品视频大全| 三级视频在线看| 午夜电影网亚洲视频| av在线播放网址| 国产手机视频一区二区| 玛丽玛丽电影原版免费观看1977 | 在线影院国内精品| 欧美波霸videosex极品| 美腿丝袜亚洲色图| 最近看过的日韩成人| 粉嫩一区二区三区在线观看| 麻豆乱码国产一区二区三区 | 国产成人在线免费观看| 日韩精品一区二区三区四| 粉嫩一区二区三区四区公司1| 久久久久久久久亚洲| 香蕉视频国产在线| 色菇凉天天综合网| sm捆绑调教视频| 国产成人av一区二区三区在线观看| 大陆av在线播放| 影视先锋久久| 91网站免费观看| 国产理论电影在线| 亚洲美女在线看| 一级特黄aaaaaa大片| 亚洲最新视频在线观看| wwwwww日本| 国内精品国产成人| 国产极品尤物在线| 欧美一区二区麻豆红桃视频| 91九色露脸| 樱花草涩涩www在线播放| 在线成人一区二区| 精品毛片一区二区三区| 欧美午夜精品久久久久久人妖| 亚洲精品视频网址| 国产精品白丝av| 亚洲熟妇av一区二区三区漫画| 精品久久久久久久久久久下田| 成人在线精品视频| 精品极品在线| 久久人人爽人人爽爽久久| 五月婷婷六月色| 在线成人av网站| 久久久久久少妇| 亚洲蜜桃精久久久久久久| 久久久久国产精品区片区无码| 麻豆视频一区二区| 成熟了的熟妇毛茸茸| 欧美韩国日本在线观看| 精品国产一区二区三区四区vr| 超薄肉色丝袜脚交一区二区| 欧美激情视频网址| 91网在线播放| 精品视频在线播放| 午夜精品久久久久久久99热黄桃 | 中文字幕一区日韩精品欧美| 日韩av无码一区二区三区不卡| 蜜臀av性久久久久蜜臀aⅴ流畅 | 警花观音坐莲激情销魂小说| 亚洲aaa级| av免费精品一区二区三区| 91福利精品在线观看| 性欧美长视频免费观看不卡| 搞黄网站在线观看| 中文精品99久久国产香蕉| 香蕉国产在线视频| 欧美tk—视频vk| 99久久精品国产一区二区成人| 色欧美乱欧美15图片| 免费一级特黄特色大片| 亚洲精品国产精华液| 中文字幕第二区| 国产亚洲视频系列| 亚洲av无码国产精品久久| 高清成人在线观看| 国产精品熟女一区二区不卡| 看片网站欧美日韩| 日韩免费高清在线| 麻豆成人精品| 黄www在线观看| 国产日韩欧美一区在线| 日韩精品在线视频免费观看| 欧美日韩91| 日本三级中文字幕在线观看| 99久久精品网| 亚洲国产午夜伦理片大全在线观看网站| 丝袜连裤袜欧美激情日韩| 国产伦精品一区二区三区四区免费| 日韩国产在线不卡视频| 1卡2卡3卡精品视频| 精品亚洲二区| 91香蕉亚洲精品| 精品国产乱码一区二区三区| 91在线中文字幕| crdy在线观看欧美| 91精品免费| 嗯用力啊快一点好舒服小柔久久| 国产成人免费电影| 成人爽a毛片| 美日韩免费视频| 国产精选一区| 亚洲精品一区二区三区av| 日韩精品影视| 亚洲美女自拍偷拍| 欧美日本三区| 日本国产在线播放| 久久久久久婷| 精品999在线| 国产精品亚洲一区二区三区在线| 亚洲av综合色区无码另类小说| 成人18视频在线播放| 久久久久久久无码| 国产清纯白嫩初高生在线观看91 | 色视频精品视频在线观看| 亚洲美女性生活视频| 97电影在线观看| 久久的精品视频| segui88久久综合9999| 日韩美女免费观看| 国产aa精品| 国产在线视频欧美一区二区三区| 亚洲精品亚洲人成在线观看| 色一情一乱一伦一区二区三区| 婷婷激情图片久久| 久青草视频在线播放| 亚洲婷婷在线| 欧洲熟妇精品视频| 国产酒店精品激情| 中文字幕丰满孑伦无码专区| 中文字幕国产一区| 精品无码一区二区三区电影桃花| 色视频成人在线观看免| va婷婷在线免费观看| 日韩精品在线播放| 丝袜美腿美女被狂躁在线观看 | 日韩和的一区二在线| 91牛牛免费视频| 亚欧洲精品视频在线观看| 亚洲日本精品一区| 一区二区三区四区五区在线| 日本高清久久久| 91丨九色丨蝌蚪丨老版| 成人一级黄色大片| 欧美性猛交xxxx富婆弯腰| 国产精品无码AV| 亚洲天堂男人天堂| 黄页网站大全在线免费观看| 国产精品伦子伦免费视频| 成人在线视频你懂的| 在线观看成人一级片| 免费国产自线拍一欧美视频| 午夜性福利视频| 国产精品久久久久久久蜜臀| 日韩av在线播| 欧美一区二区三区免费视频| 狠狠色伊人亚洲综合网站l | 九九热这里有精品| 国产综合欧美在线看| 欧美~级网站不卡| 中文久久久久久| 91视视频在线直接观看在线看网页在线看 | 欧美成人一区二区在线观看| 国产精一区二区三区| 337人体粉嫩噜噜噜| 五月激情综合色| 农村少妇久久久久久久| 美女精品久久久| 精品久久毛片| 人偷久久久久久久偷女厕| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲第一区中文字幕| 先锋成人av| 亚洲伊人第一页| **女人18毛片一区二区| 亚洲少妇久久久| 国产丝袜欧美中文另类| 国产免费观看av| 日韩国产精品一区| 999福利在线视频| 国产有色视频色综合| 欧美日韩精选| 美女又黄又免费的视频| 亚洲免费观看高清| 国产黄a三级三级看三级| 久久国产精品久久久久| 亚洲精品三区| 久久精品国产精品亚洲精品色| 久久国产精品第一页| youjizz亚洲女人| 欧美三级中文字幕在线观看| 国产在线高清| 国产精品热视频| 999国产精品视频| 911av视频| 亚洲美女屁股眼交3| а√中文在线资源库| 欧美激情视频在线免费观看 欧美视频免费一 | 国产99视频精品免费视频36| 狠狠综合久久| av在线播放网址| 欧美午夜激情在线| 成年人在线视频免费观看| 国产精品女人网站| 天天揉久久久久亚洲精品| 性生交大片免费看l| 亚洲第一主播视频| 欧美zozo| 成人av在线亚洲| 欧美国产三级| chinese麻豆新拍video| 欧美午夜久久久| 二区三区在线| 亚洲xxxx3d| 亚洲午夜伦理| 成年人免费观看视频网站| 在线观看国产91| 黄av在线免费观看| 国产精品加勒比| 狂野欧美一区| 三级在线观看免费大全| 亚洲成在人线av| 国产私拍福利精品视频二区| 在线码字幕一区| 成人a免费在线看| 香蕉污视频在线观看| 久久精品美女视频网站| 国产精品巨作av| av在线无限看| 亚洲一区二区黄色| 国产三级视频在线看| 亚洲专区国产精品| 欧美一区=区| 国模无码国产精品视频| 亚洲欧美激情四射在线日| 综合久久av| 日韩av黄色网址| 日韩理论片在线| 日漫免费在线观看网站| 亚洲淫片在线视频| 日韩精品亚洲专区| 国产精品18p| zzijzzij亚洲日本成熟少妇| 群体交乱之放荡娇妻一区二区 |