Java在誕生時就選擇了對內置多線程的支持,線程作為操作系統的`最小調度單位`,能夠更好的利用多核cpu的優勢,顯著提高程序的運行性能。
## 線程
現代操作系統在運行程序的時候都會創建一個進程,在進程里面創建運行程序所需要的調度單位,就是一個線程,一個進程可以包含多個線程。每個線程都有自己的程序計數器,棧和局部變量表等屬性。Java中的線程和操作系統中的線程是一一對應的,當運行一個main方法的時候會創建一個java進程,其包含的線程如下:
~~~
?// 獲取Java線程管理MxBean
?ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
?// 不需要獲取同步的synchronized和monitor信息
?ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
?for (ThreadInfo threadInfo : threadInfos) {
? ? ?System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
?}
~~~
結果:
~~~
?[1] main # main線程
?[2] Reference Handler # 清除引用的線程
?[3] Finalizer # 調用finalize方法的線程
?[4] Signal Dispatcher # 分發處理發送給JVM信號的線程
?[5] Attach Listener
?[12] Common-Cleaner
?[13] Monitor Ctrl-Break
?[14] Notification Thread
~~~
備注:上述是基于JDK14的測試結果,不通過版本可能不太一致。
### 線程的使用
java使用Thread類實例來表示一個線程,所有的線程對象都是Thread類的實例或是其子類的實例。Thread類繼承Runnable接口,并實現該接口的唯一一個抽象方法run(),在該方法中執行線程要運行的代碼。
線程的創建方式有如下幾種:
#### 繼承Thread類創建線程
1. 自定義線程類
~~~
?public class MyThread extends Thread {
? ? ?public MyThread() {}
? ? ?/**
? ? ?* 帶參構造,可以指定該線程的名稱
? ? ?*/
? ? ?public MyThread(String name) {
? ? ? ? ?super(name); //調用父類Thread的構造方法指定線程名稱
? ? }
? ? ?
? ? ?/**
? ? ?* 重寫run方法 -- 必須
? ? ?*/
? ? ?@Override
? ? ?public void run() {
? ? /* 線程執行的代碼 */
? ? }
?}
~~~
2. 創建實例
~~~
?public class Test {
? ? ?public static void main(String[] args) {
? ? ? ? ?MyThread myThread = new MyThread("新線程名稱"); //創建一個線程實例
? ? ? ? ?myThread.start(); //開啟新線程 -- 執行線程中run方法的內容
? ? }
?}
~~~
> 注意:要調用start方法才會自動執行run中的方法,線程并不是調用run方法來執行。
~~~
?調用start()方法開啟一個新線程的時候,并不是立刻就會執行該線程中run中的代碼。因為java是采用搶占式調度的方式來分配CPU的使用,新創建的線程在不設置其優先級的情況下優先級是相同的。因此在調用start方法之后,該線程參與CPU使用權的搶奪,和main線程搶奪或者是和另外創建的線程搶奪,能不能搶奪到還是一回事,**只有搶奪到了才會執行該線程對應的run方法。**
~~~
Thread類常用方法
**構造方法**
~~~
?// 無參構造,創建一個新的Thread對象
?public Threa();
?// 傳入Runnable接口實現類對象,在啟動線程的時候調用target接口的run方法。
?public Thread(Runnable target);
?// 自定義線程名稱
?public Thread(String name);
?// 傳入Runnable接口對象,并自定義線程名稱
?public Thread(Runnable target, String name);
~~~
**常用的基礎方法**
~~~
?// 獲取當前線程名稱
?public String getName();
?// 當前正在執行的線程停止執行指定的毫秒數,時間一到就自動CPU就自動恢復并繼續執行下去。
?public static void sleep(long millis);
?// 設置此此線程的優先級,有些操作系統會忽略優先級的設置,因此并不能根據優先級來編排程序的運行順序。
?public final void setPriority(int newPriority);
?// 獲取當前正在執行的線程
?public static Thread currentThread();
~~~
#### 繼承Runnable類創建
在Thread的構造方法中,有一個**public Thread(Runnable target)**的構造方法,即傳入Runnable接口的實現類對象,重寫里面的run方法創建一個新線程。值得注意的是,Thread類本身就是實現Runnable接口的,Runnable接口只有一個run方法(函數式接口)。
1. 定義Runnable實現類接口
~~~
?public class RunnableDemo implements Runnable{
? ? ?@Override
? ? ?public void run() { //重寫run方法
? ? ? ? ?/* 線程執行代碼 */
? ? }
?}
~~~
2. 創建實例
~~~
?public static void main(String[] args) {
? Runnable run = new RunnableDemo();
? Thread thread = new Thread(run);
? thread.start(); //注意最后要調用start方法才會開啟該多線程
?}
~~~
##### 使用匿名內部類的方式
上面創建Runnable接口實現類的時候可以發現,創建實現類僅僅是為了重寫其run方法,如果要在線程中執行的代碼有各種各樣的話,就要創建各種各樣的實現類,還只是重寫其run方法,反倒有點復雜了。
而使用匿名內部類的方式可以更方便的創建一個Thread接口:
~~~
?Thread thread = new Thread(new Runnable() {
? ? ?@Override
? ? ?public void run() {
? ? System.out.println("這是一個匿名內部類");
? ? }
?});
?thread.start();
?//也可以
?new Thread(new Runnable() {
? ? ?@Override
? ? ?public void run() {
? ? System.out.println("這是一個匿名內部類");
? ? }
?}).start();
~~~
##### 使用Lambda表達式創建
在JDK8中引入了Lambda表達式的新特性,有關于Lambda表達式會在以后中講到,先來看看怎么用:
~~~
?new Thread(()->{
? /* 這里即可書寫run方法中的內容 */
?}).start();
~~~
**使用Thread和Runnable的區別**
1. Java中Thread實例才表示一個線程,Runnable中的run方法表示需要執行的任務。
2. 使用Runnable接口是很容易實現資源共享的,同時可以實現代碼實現和線程相互獨立。
3. 后面的線程池中只能放入實現Runnable接口的類,不能直接放入Thread類。
4. 可以使用Lambda表達式,代碼書寫更加簡便。
## 線程的狀態
與操作系統中進程的狀態類似,Java中也定義了一套與線程有關的狀態,共有6種狀態,用ThreadState枚舉來表示

* NEW:初始狀態,線程被構建,但是還沒有調用**start方法**。
* RUNNABLE:運行狀態,調用了start方法,Java將就緒和運行統稱為“運行中”。
* BLOCKED:阻塞狀態,從運行態中競爭不到synchronized鎖時進入阻塞狀態;獲取到鎖之后又重新恢復到運行態。
* TERMINATED:終止狀態,run方法中代碼執行完畢后進入終止狀態。
* WAITING:等待狀態,運行態調用`wait`、`join`、`LockSupport.park`方法時進入等待狀態。調用`notify`、`notifyAll`、`LockSupport.unpark(Thread)`方法時恢復到運行態。*\*由自己進入等待,由其他線程喚醒!*
* TIMED\_WAITING:超時等待狀態,可以設置等待之間,時間到了自動恢復到運行態。
- 第一章 Java基礎
- ThreadLocal
- Java異常體系
- Java集合框架
- List接口及其實現類
- Queue接口及其實現類
- Set接口及其實現類
- Map接口及其實現類
- JDK1.8新特性
- Lambda表達式
- 常用函數式接口
- stream流
- 面試
- 第二章 Java虛擬機
- 第一節、運行時數據區
- 第二節、垃圾回收
- 第三節、類加載機制
- 第四節、類文件與字節碼指令
- 第五節、語法糖
- 第六節、運行期優化
- 面試常見問題
- 第三章 并發編程
- 第一節、Java中的線程
- 第二節、Java中的鎖
- 第三節、線程池
- 第四節、并發工具類
- AQS
- 第四章 網絡編程
- WebSocket協議
- Netty
- Netty入門
- Netty-自定義協議
- 面試題
- IO
- 網絡IO模型
- 第五章 操作系統
- IO
- 文件系統的相關概念
- Java幾種文件讀寫方式性能對比
- Socket
- 內存管理
- 進程、線程、協程
- IO模型的演化過程
- 第六章 計算機網絡
- 第七章 消息隊列
- RabbitMQ
- 第八章 開發框架
- Spring
- Spring事務
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 數據庫
- Mysql
- Mysql中的索引
- Mysql中的鎖
- 面試常見問題
- Mysql中的日志
- InnoDB存儲引擎
- 事務
- Redis
- redis的數據類型
- redis數據結構
- Redis主從復制
- 哨兵模式
- 面試題
- Spring Boot整合Lettuce+Redisson實現布隆過濾器
- 集群
- Redis網絡IO模型
- 第十章 設計模式
- 設計模式-七大原則
- 設計模式-單例模式
- 設計模式-備忘錄模式
- 設計模式-原型模式
- 設計模式-責任鏈模式
- 設計模式-過濾模式
- 設計模式-觀察者模式
- 設計模式-工廠方法模式
- 設計模式-抽象工廠模式
- 設計模式-代理模式
- 第十一章 后端開發常用工具、庫
- Docker
- Docker安裝Mysql
- 第十二章 中間件
- ZooKeeper