點贊關(guān)注玛痊,不再迷路赴恨,你的支持對我意義重大!
?? Hi箕憾,我是丑丑牡借。本文 「Java 路線」| 導(dǎo)讀 —— 他山之石,可以攻玉 已收錄袭异,這里有 Android 進階成長路線筆記 & 博客钠龙,歡迎跟著彭丑丑一起成長。(聯(lián)系方式在 GitHub)
前言
- 并發(fā)編程 是面試的重點,同時也是 Java 開發(fā)從入門到精通遇到的第一個坎俊鱼;
- 這篇文章是 Java 并發(fā)系列的第一篇文章刻像,為了幫助小白們漸進深入,我們先來介紹并發(fā)編程中的基礎(chǔ)概念并闲。如果能幫上忙细睡,請務(wù)必點贊加關(guān)注,這真的對我非常重要帝火。
目錄
1. 基本概念
1.1 進程與線程
- 1溜徙、進程是操作系統(tǒng)進行資源分配的最小單位,而線程是 CPU 調(diào)度的最小單位犀填;
- 2蠢壹、一個進程里可以有多個線程,線程必須依賴于進程存在九巡;
- 3图贸、不同進程之間地址空間和資源是獨立的,同一進程中的線程共享進程的地址空間和資源冕广,但不共享線程工作空間疏日。
1.2 CPU 與處理器
1、根據(jù)摩爾定律撒汉,集成電路上可以容納的晶體管數(shù)目在大約每經(jīng)過 24 個月便會增加一倍沟优。但是當晶體管大小降低到一定程度會出現(xiàn) 「量子隧穿」,無法繼續(xù)提高晶體管的密度睬辐。因此挠阁,發(fā)展出了 單芯片多處理器技術(shù) ,即多個處理器集成到同一個 CPU 芯片上溯饵,各個處理器處理不同的線程侵俗。所謂四核 CPU 值得就是一顆 CPU 擁有四個處理器。
2瓣喊、一般情況下茉帅,一個處理器只能處理一個線程箍铲,Intel 研發(fā)的 「超線程」 技術(shù)可以使得一個處理器處理兩個線程,讓單個處理器就能使用多線程「并行」操作济瓢。
1.3 CPU 時間片輪轉(zhuǎn)機制
時間片輪轉(zhuǎn)機制(又稱 Round-Robin跪者,RR 調(diào)度)棵帽,是用于分時系統(tǒng)的線程調(diào)度機制,要點如下:
- 1渣玲、調(diào)度程序根據(jù)線程優(yōu)先級搶占線程的 CPU 時間片逗概;
- 2、線程結(jié)束忘衍、阻塞或者讓出時間片時逾苫,CPU 會切換執(zhí)行線程(讓出時間片后依然有可能重新獲得)卿城;
- 3、時間片過長會降低響應(yīng)性铅搓,時間片過短會增加過多線程切換瑟押,降低 CPU 吞吐量。
易混淆: 線程的時間片分配是搶占式的星掰,而線程間的工作是協(xié)作式的多望。
1.4 并行 & 并發(fā)
并行(Parallel): 指多個 CPU 核心分別執(zhí)行不同線程,兩個線程之間不搶占 CPU 資源氢烘,可以同時運行怀偷;
并發(fā)(Concurrent): 指一個 CPU 核心交替執(zhí)行不同線程,從單位時間的宏觀角度看播玖,多個線程開起來是同時運行的椎工。(討論并發(fā)一定要約定單位時間)
1.5 并發(fā)編程注意事項
線程安全
線程死鎖
線程過多
OS限制:一個進程Linus最大線程1000 window最大線程2000
線程:棧空間(缺省1M)蜀踏、文件描述符/句柄1024
- 線程饑餓
Editting...
2. Java 中的線程
2.1 執(zhí)行 main() 方法晋渺,會啟動幾個線程?
總所周知脓斩,main() 方法是 Java 程序的入口木西,運行在主線程(線程名:main)。事實上随静,除了主線程外八千,虛擬機同時還啟動了其他子線程。我們可以通過以下代碼打印運行的線程:
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for(ThreadInfo threadInfo:threadInfos) {
System.out.println("["+threadInfo.getThreadId()+"]"+" " +threadInfo.getThreadName());
}
輸出如下:
線程 | 作用 |
---|---|
[1] main | 主線程 |
[2] Reference Handler | 清除 Reference |
[3] Finalizer | 調(diào)用 finalize() 方法 |
[4] Signal Dispatcher | 分發(fā)處理發(fā)送給 JVM 信號 |
[5] Attach Listener | 內(nèi)存 dump燎猛,線程 dump恋捆,類信息統(tǒng)計,獲取系統(tǒng)屬性等 |
[6] Monitor Ctrl-Break | 監(jiān)控 Ctrl-Break 中斷信號 |
- [1] 主線程是程序的入口重绷;
- [2] [3] 與引用類型和 finalize()沸停,具體見 「Java 路線」| 引用類型 & Finalizer 機制;
- 其他幾個線程筆者不了解昭卓。
2.2 啟動線程的方式
提示: 啟動線程的方式有人認為有兩種愤钾,有人認為有三種。建議不必糾結(jié)于結(jié)論候醒,關(guān)注是否自圓其說即可能颁。
根據(jù) JDK 官方在java.lang.Thread.java
中的注釋,存在 兩種啟動線程 的方式:
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread.
...
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started.
- 1倒淫、繼承 Thread 類伙菊,重寫 run(),隨后調(diào)用 Thread#start();
- 2镜硕、實現(xiàn) Runnable 接口运翼,重寫 run(),并關(guān)聯(lián) Thread兴枯,隨后調(diào)用 Thread#start()
有的說法會將java.util.concurrent.Callable
也列為一種啟動方式南蹂,其實 Callable 只是 java.util.concurrent.FutureTask
的任務(wù)接口,而 FutureTask 本身就是 Runnable 的子類念恍,所以「兩種方式」的說法更有說服力六剥。
FutureTask<V> ->RunnableFuture<V> -> Runnable
Thread 和 Runnable 的區(qū)別
Thread 是 Java 線程的抽象,Runnable 是對任務(wù)的抽象峰伙,與線程沒有直接關(guān)系疗疟。Thread 可以接受任意一個 Runnable 的實例并執(zhí)行。
start() 和 run() 的區(qū)別
start() 是啟動一個線程瞳氓,讓一個線程進入「就緒狀態(tài)」等待分配 CPU 時間片策彤,分到時間片后執(zhí)行 run() 方法,start() 方法重復(fù)執(zhí)行會拋出異常匣摘;而 run() 方法是承載業(yè)務(wù)邏輯的地方店诗,本質(zhì)上是一個普通的實例方法,可以重復(fù)執(zhí)行音榜。
2.3 線程終止的方式
- 1庞瘸、自然終止
當Thread#run()
執(zhí)行結(jié)束,或者執(zhí)行過程拋出了未捕獲的異常赠叼,則線程 自然終止擦囊。
- 2、stop()
線程停止嘴办、暫停和恢復(fù)分別對應(yīng)suspend () & resume() & stop()
瞬场,需要注意的是,這些 API 都是 過時的涧郊。
suspend()
:暫停線程贯被,當前線程不會釋放鎖資源,而是占有鎖進入睡眠狀態(tài)妆艘,容易引發(fā)死鎖彤灶;stop()
:停止線程,沒有給予線程釋放資源的時機双仍。3枢希、中斷
中斷本質(zhì)上不屬于終止線程的方式,但是往往 中斷會給予線程終止的時機朱沃。例如在sleep() 、wait()
等方法上阻塞的線程,就可以利用中斷來退出等待逗物,進而判斷是否應(yīng)該終止線程的業(yè)務(wù)邏輯搬卒。關(guān)于中斷的具體介紹,見「Java 路線」| 線程協(xié)作機制翎卓。
提示: 可以響應(yīng)中斷的方法往往聲明
throws InterruptedException
契邀。
3. 線程優(yōu)先級
Edigging...
4. 守護線程
4.1 定義
守護(Daemon)線程是一種支持型線程,當虛擬機中所有非守護線程都終止時失暴,虛擬機就會退出坯门,不理會守護線程是否終止。
例如 第 2.1 節(jié) 中提到的 Reference Handler 線程和 Finalizer 線程就是守護線程逗扒。通過Thead#setDaemon(true)
可以設(shè)置守護線程古戴,
4.2 finally{} 塊一定會執(zhí)行嗎?
一般來說矩肩,try-catch-finally 代碼塊中 finally 塊是一定會執(zhí)行的现恼,除了以下特殊情況,finally{} 塊不一定會執(zhí)行:
- 1黍檩、finally{} 執(zhí)行之前調(diào)用
System.exit(0)
:虛擬機都推出了叉袍,finally{} 塊當然也沒機會執(zhí)行了; - 2刽酱、守護線程:當虛擬機中所有非守護線程都終止時喳逛,虛擬機就會退出,這個時候守護線程中的 finally{} 塊也有可能不會執(zhí)行棵里。
掌握了并發(fā)編程的基本概念艺配,下面我們就可以愉快地深入交流了,所有精彩內(nèi)容盡在: 「Java 路線」| 導(dǎo)讀 —— 他山之石衍慎,可以攻玉 转唉,常來玩~
創(chuàng)作不易,你的「三連」是丑丑最大的動力稳捆,我們下次見赠法!