主頁 > 後端開發 > Java并發編程實踐

Java并發編程實踐

2020-10-01 12:07:17 後端開發

最近閱讀了《Java并發編程實踐》這本書,總結了一下幾個相關的知識點,

執行緒安全

當多個執行緒訪問某個類時,不管運行時環境采用何種調度方式或者這些執行緒將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是執行緒安全的,可以通過原子性一致性不可變物件執行緒安全的物件加鎖保護同時被多個執行緒訪問的可變狀態變數來解決執行緒安全的問題,

可見性

在沒有同步的情況下,編譯器、處理器以及運行時等都可能對操作的執行順序進行一些意想不到的調整,在缺乏足夠同步的多執行緒程式中,要想對記憶體操作的執行順序進行判斷,幾乎無法得出正確的結論,加鎖的含義不僅僅局限于互斥行為,還包括記憶體可見性,為了確保所有執行緒都能看到共享變數的最新值,所有執行讀寫操作的執行緒都必須持有同一把鎖,volatile變數不會被快取在暫存器或者對其他處理器不可見的地方,因此在讀取volatile型別的變數時總會回傳最新寫入的值,volatile變數是一種比synchronized關鍵字更輕量級的同步機制,加鎖機制即可以確保可見性又可以確保原子性,而volatile變數只能確保可見性

發布逸出

當從物件的建構式中發布物件時,只是發布了一個尚未構造完成的物件,即使發布物件的陳述句位于建構式的最后一行也是如此,如果this參考在建構式中逸出,那么這種現象就被認為是不正確構造,常見的逸出有,在建構式中創建并啟動一個執行緒、內部私有可變狀態逸出等,
要安全地發布一個物件,物件的參考以及物件的狀態必須同時對其他執行緒可見,一個正確構造的物件可以通過一下方式來安全地發布:

  • 在靜態初始化函式中初始化一個物件參考
  • 將物件的參考保存到volatile型別的域或者AtomicReference物件中
  • 將物件的參考保存到某個正確構造物件的final型別域中
  • 將物件的參考保存到一個由鎖保護的域中

物件的發布需求取決于它的可變性:

  • 不可變物件可以通過任意機制來發布
  • 事實不可變物件必須通過安全方式來發布
  • 可變物件必須通過安全方式發布,并且必須是執行緒安全的或者由某個鎖保護起來

千萬不要在A執行緒中創建物件,在B執行緒中使用該物件,在物件初始化的時候,首先會去申請一個記憶體空間,然后給物件中的屬性賦默認值(如:int型別的變數默認值為0等),再通過建構式或者代碼塊對屬性進行賦值,最后地址空間指向的物件才算是創建完成了(當然還有很多其他的步驟,這里只是簡單說明一下),這樣很有可能出現B執行緒獲取到的物件是不完整的,因為Java執行緒模型的和物件的可見性的原因,

執行緒中斷

呼叫Thread.interrupt()并不意味著立即停止目標執行緒正在進行的作業,而只是傳遞了請求中斷的訊息,

對中斷操作的正確理解是:它并不是真正地中斷一個正在運行的執行緒,而只是發出中斷請求,然后由執行緒在下一個合適的時刻中斷自己,(這些時刻也被稱為取消點),有些方法,例如:Object.wait()Thread.sleep()Thread.join()等,將嚴格地處理這種請求,當它們收到中斷請求或者在開始執行時發現某個已被設定好的中斷狀態時,將拋出一個例外,

在使用靜態的interrupted時應該小心,因為它會清除當前執行緒的中斷狀態,如果在呼叫interrupted時回傳了true,那么除非你想屏蔽這個中斷,否則必須對它進行處理—可以拋出InterruptedException,或者通過再次呼叫interrupt()來恢復中斷狀態,Future.cancel()方法可以取消執行緒,

通常,中斷是實作取消的最合理方式

未捕獲的例外

在運行時間較長的應用程式中,通常會為所有執行緒的未捕獲例外指定同一個例外處理器(實作Thread.UncaughtExceptionHandler介面),并且該處理器至少會將例外資訊記錄到日志中,

如果你希望在任務由于發生例外和失敗時獲得通知,并且執行一些特定于任務的居處操作,那么可以將任務封裝在能捕獲例外的RunnableCallable中,或者改寫ThreadPoolExecutor.afterExecute()方法,
只有通過execute()提交的任務,才能將它拋出的例外交給未捕獲例外處理器,而通過submit提交的任務的例外都被封裝在Future.get()ExecutionException中重新拋出,

JVM關閉

關閉鉤子是指通過Runtime.addShutdownHook注冊的但尚未開始的執行緒,JVM并不能保證關閉鉤子的呼叫順序,在關閉應用程式執行緒時,如果有(守護或非守護)執行緒仍然在運行,那么這些執行緒接下來將與關閉行程并發執行,當所有的關閉鉤子都執行結束時,如果runFinalizersOnExittrue,那么JVM將運行終結器,然后再停止,

關閉鉤子應該是執行緒安全的,它們在訪問共享資料時必須使用同步機制,并且小心地避免發生死鎖,這與其他并發代碼的要求相同,而且,關閉鉤子不應該對應用程式的狀態或者JVM的關閉原因做出任何假設,因此在撰寫關閉鉤子的代碼時必須考慮周全,

關閉ExecutorService

ExecutorService提供了兩種關閉方法:

  • ExecutorService.shutdown():正常關閉
  • ExecutorService.shutdownNow():強行關閉
    這兩種關閉方式的差別在于各自的安全性回應性:強行關閉的速度更快,但風險也更大,因為任務很可能在執行到一半時被結束;而正常關閉雖然速度慢,但卻更安全,因為ExecutorService會一直等到佇列中的所有任務都執行完成后才關閉,在其他擁有執行緒的服務中也應該考慮提供類似的關閉方式以供選擇,
  1. 正常關閉
try{
    // 正常關閉
    executorService.shutdown(); 
    // 等待指定時間直到結束,超時會拋出InterruptedException例外
    executorService.awaitTermination(timeout, unit); 
}catch(InterruptedException ex){
    // do something
}
  1. 強行關閉
try{
    // 強行關閉
    List<Runnable> unfinishedTasks = executorService.shutdownNow(); 
    // 處理未完成的任務
    handle(unfinishedTasks);
    // 等待指定時間直到結束,超時會拋出InterruptedException例外
    executorService.awaitTermination(timeout, unit); 
}catch(InterruptedException ex){
    // do something
}

資源釋放

呼叫的方法 CPU
Thread.sleep() 不釋放 釋放
Thread.join() 不釋放 釋放
Thread.yield() 不釋放 釋放
Object.wait() 釋放 釋放
Condition.await() 釋放 釋放

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 
  • corePoolSize 執行緒池核心執行緒大小

在創建了執行緒池后,默認情況下,執行緒池中并沒有任何執行緒,而是等待有任務到來才創建執行緒去執行任務,(除非呼叫了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建執行緒的意思,即在沒有任務到來之前就創建corePoolSize個執行緒或者一個執行緒),

默認情況下,在創建了執行緒池后,執行緒池中的執行緒數為0,當有任務來之后,就會創建一個執行緒去執行任務,當執行緒池中的執行緒數目達到corePoolSize后,就會把到達的任務放到快取佇列當中,核心執行緒在allowCoreThreadTimeout被設定為true時會超時退出,默認情況下不會退出,

  • maximumPoolSize 執行緒池最大執行緒數

當執行緒數大于或等于核心執行緒,且任務佇列已滿時,執行緒池會創建新的執行緒,直到執行緒數量達到maximumPoolSize,如果執行緒數已等于maximumPoolSize,且任務佇列已滿,則已超出執行緒池的處理能力,執行緒池會拒絕處理任務而拋出例外,

  • keepAliveTime 空閑執行緒存活時間

當執行緒空閑時間達到keepAliveTime,該執行緒會退出,直到執行緒數量等于corePoolSize,如果allowCoreThreadTimeout設定為true,則所有執行緒均會退出直到執行緒數量為0

  • unit 空間執行緒存活時間單位

keepAliveTime的計量單位

  • workQueue 作業佇列

新任務被提交后,會先進入到此作業佇列中,任務調度時再從佇列中取出任務,JDK中提供了四種作業佇列:

  1. ArrayBlockingQueue 基于陣列的有界阻塞佇列,按FIFO排序,新任務進來后,會放到該佇列的隊尾,有界的陣列可以防止資源耗盡問題,當執行緒池中執行緒數量達到corePoolSize后,再有新任務進來,則會將任務放入該佇列的隊尾,等待被調度,如果佇列已經是滿的,則創建一個新執行緒,如果執行緒數量已經達到maximumPoolSize,則會執行拒絕策略,

  2. LinkedBlockingQuene 基于鏈表的無界阻塞佇列(其實最大容量為Interger.MAX),按照FIFO排序,由于該佇列的近似無界性,當執行緒池中執行緒數量達到corePoolSize后,再有新任務進來,會一直存入該佇列,而不會去創建新執行緒直到maximumPoolSize,因此使用該作業佇列時,引數maximumPoolSize其實是不起作用的,

  3. SynchronousQuene 一個不快取任務的阻塞佇列,生產者放入一個任務必須等到消費者取出這個任務,也就是說新任務進來時,不會快取,而是直接被調度執行該任務,如果沒有可用執行緒,則創建新執行緒,如果執行緒數量達到maximumPoolSize,則執行拒絕策略,

  4. PriorityBlockingQueue 具有優先級的無界阻塞佇列,優先級通過引數Comparator實作,

  • threadFactory 執行緒工廠

創建一個新執行緒時使用的工廠,可以用來設定執行緒名是否為daemon執行緒Thread.UncaughtExceptionHandler等等,

  • handler 拒絕策略

當作業佇列中的任務已到達最大限制,并且執行緒池中的執行緒數量也達到最大限制,這時如果有新任務提交進來,該如何處理呢,這里的拒絕策略,就是解決這個問題的,JDK中提供了4中拒絕策略:

  1. CallerRunsPolicy 該策略下,在呼叫者執行緒中直接執行被拒絕任務的run方法,除非執行緒池已經shutdown,則直接拋棄任務,

  2. AbortPolicy 該策略下,直接丟棄任務,并拋出RejectedExecutionException例外,

  3. DiscardPolicy該策略下,直接丟棄任務,什么都不做,

  4. DiscardOldestPolicy 該策略下,拋棄進入佇列最早的那個任務,然后嘗試把這次拒絕的任務放入佇列

條件佇列

條件佇列使得一組執行緒(稱之為等待執行緒集合)能夠通過某種方式來等待特定的條件變成真,傳統佇列的元素是一個個資料,而與之不同的是,條件佇列中的元素是一個個正在等待相關條件的執行緒,

正如每個Java物件都可以作為一個鎖,每個物件同樣可以作為一個條件佇列,并且ObjectwaitnotifynotifyAll方法就構成了內部條件佇列的API,物件的內置鎖與其內部條件佇列是相互關聯的,要呼叫物件X中條件佇列的任何一個方法,必須持有物件X上的鎖,這就是因為“等待由狀態構成的條件”與“維護狀態一致性”這兩種機制必須被緊密地系結在一起:只有能對狀態進行檢查時,才能在某個條件上等待,并且只有能修改狀態時,才能從條件等待中釋放一個執行緒,

當使用條件等待時(例如Object.waitCondition.await

  • 通常都有一個條件謂詞,包括一些物件狀態的測驗,執行緒在執行前必須首先通過這些測驗
  • 在呼叫wait之前測驗條件謂詞,并且從wait中回傳是再次進行測驗
  • 在一個回圈中呼叫wait
  • 確保使用與條件佇列相關的鎖來保護構成條件謂詞的各個狀態變數
  • 當呼叫waitnotifynotifyAll等方法時,一定要持有與條件佇列相關的鎖
  • 在檢查條件謂詞之后以及開始執行相應的操作之前,不要釋放鎖

降低鎖競爭程度的幾種方式

  • 減少鎖的持有時間
  • 降低鎖的請求頻率
  • 使用帶有協調機制的獨占鎖,這些機制允許更高的并發性

CAS操作

CAS包含3個運算元:需要讀寫的記憶體位置V、進行比較的值A和擬寫入的新值B,當且僅當V的值等于A時,CAS才會通過原子方式用新值B來更新V的值,否則不會執行任何操作,無論位置V的值是否等于A,都將回傳V原有的值,

CAS的主要缺點是:它將使呼叫者處理競爭問題(通過重試、回退、放棄),而在鎖中能自動處理競爭問題,同時CAS還會出現ABA的問題,

Java記憶體模型(JMM)

在共享記憶體的多處理器體系架構中,每個處理器都擁有自己的快取,并且定期地與主記憶體進行協調,在不同的處理器架構中提供了不同級別的快取一致性(Cache Coherence),其中一部分只提供最小的保證,即允許不同的處理器在任意時刻從同一個存盤位置上看到不同的值,作業系統、編譯器以及運行時(有時甚至包括應用程式)需要彌合這種硬體能力與執行緒安全需求之間的差異,

Java記憶體模型是通過各種操作來定義的,包括對變數的讀寫操作,監視器的加鎖和釋放操作,以及執行緒啟動和合并操作,JMM為程式中所有的操作定義了一個偏序關系,稱之為Happens-Before,如果兩個操作之間缺乏Happens-Before關系,那么JVM可以對它們任意的重排序,

當一個變數被多個執行緒讀取并且至少被一個執行緒寫入時,如果在讀操作和寫操作之間沒有依照Happens-Before來排序,那么就會產生資料競爭的問題,在正確同步的程式中不存在資料競爭,并會表現出串行一致性,這意味著程式中的所有操作都會按照一種固定的和全域的順序執行,

Happens-Before的規則包括

  • 程式順序規則,如果程式中操作A在操作B之前,那么在執行緒中A操作將在B操作之前執行,
  • 監視器鎖規則,在監視器鎖上的解鎖操作必須在同一個監視器鎖上的加鎖操作之前執行,
  • volatile變數規則, 對volatile變數的寫入操作必須在對該變數的讀操作之前執行,
  • 執行緒啟動規則,在執行緒上對Thread.start()的呼叫必須在該執行緒中執行任何操作之前執行,
  • 執行緒結束規則,執行緒中的任何操作都必須在其他執行緒檢測到該執行緒已經結束之前執行,或者從Thread.join() 中成功回傳,或者在呼叫Thread.isAlive()時回傳false
  • 中斷規則,當一個執行緒在另一個執行緒上呼叫interrupt時,必須在被中斷執行緒檢測到interrupt呼叫之前執行(通過拋出InterruptedException,或者呼叫isInterruptedinterrupted),
  • 終結器規則,物件的建構式必須啟動在該物件的終結器之前執行完成,
  • 傳遞性,如果操作A在操作B之前執行,并且操作B在操作C之前執行,那么操作A必須在操作C之前執行,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/145063.html

標籤:Java

上一篇:流量轉發映射

下一篇:無鎖、自旋鎖、偏向鎖、輕量級鎖和重量級鎖

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more