多线程并发 (二) 了解 Thread
章節:
多線程并發 (一) 了解 Java 虛擬機 - JVM
多線程并發 (二) 了解 Thread
多線程并發 (三) 鎖 synchronized、volatile
多線程并發 (四) 了解原子類 AtomicXX 屬性地址偏移量,CAS機制
?
通過上一篇?多線程并發 (一) 了解 Java 虛擬機 - JVM?了解了java 虛擬機的構成以及對象的創建等。從Java虛擬機棧我們知道每當我們創建一個線程JVM就會給我們的線程分配一個私有的內存空間和程序計數器記錄當前線程運行的次行代碼的地址。了解了Thread之后,通過多線程編程進而引入鎖(下篇)的概念。
1. 線程簡介
線程是進程中可獨立執行的最小單位,也是 CPU 資源分配的基本單位。
2. 線程的四個屬性
????(1)編號Id
??????????????線程的編號(id)用于標識不同的線程,每條線程擁有不同的編號。
????(2)?名字
?????????????每個線程都有自己的名字(name),名字的默認值是 Thread-線程編號,比如 Thread-0 。除了默認值,我們也可以給線程設置名字,以我們自己的方式去區分每一條線程。
????(3)類別
????????????分為守護線程和用戶線程,我們可以通過 setDaemon(true) 把線程設置為守護線程。GC線程是守護線程,對于守護線程JVM退出時不會考慮次線程是否持行完畢。
????(4)優先級
????????????線程的優先級(Priority)用于表示應用希望優先運行哪個線程,線程調度器會根據這個值來決定優先運行哪個線程。
????????????Java 中線程優先級的取值范圍為 1~10,默認值是 5,Thread 中定義了下面三個優先級常量。
????????????最低優先級:MIN_PRIORITY = 1
????????????默認優先級:NORM_PRIORITY = 5
????????????最高優先級:MAX_PRIORITY = 10
3. 線程的六個方法
線程的常用方法有六個,它們分別是三個非靜態方法 start()、run()、join() 和三個靜態方法 currentThread()、yield()、sleep() 。我們只關注start()、join()、yield()、sleep(),另外run()、currentThread()容易理解。
(1)yield()?
??????顧名思義yield英文 讓 的意思。當t.yield() 持行之后?t 回到就緒狀態,所有線程重新搶用cpu。
(2)join()?
??????t2正在運行,這時在t2線程中調用 t1.join() ,這是讓t2 等待線程t1 結束了,再運行。次方法可以讓線程按順序持行。自己線程中調用自己的join方法是無意義的。
(3)sleep()
??????暫停阻塞等待一段時間,時間過了就繼續。這個方法是不釋放鎖的。
(4)start()
?????啟動線程,讓cpu調度器調度開啟線程持行run()方法。而直接調用run()方法只是運行當前run方法,不會開啟線程。
4.線程運行狀態
線程調用狀態圖
分析:1. new 的狀態(NEW)
???????????2. start() 之后線程到達就緒狀態等待cpu調度,cpu調度了 就是運行狀態。(RUNNABLE)
???????????3. 調用了 TImedWaiting 就回到就緒狀態,結束之后自動到運行狀態(TIMED_WAITING)
???????????4. 調用 Waiting 就緒然后持行相應的方法到運行(WAITING)
???????????5. 調用 Blocked 等待進入同步代碼快的鎖。(BLOCKED)
???????????6. 結束狀態Teminated(TERMINATED)
線程被掛起:正在cpu中調度的線程,cpu把該線程丟出去,持行其他線程,這就是線程被掛起。之后又回來持續該線程。(cpu每次只能持行一個線程)
5.Java 線程調度原理
???????在任意時刻,CPU 只能執行一條機器指令,每個線程只有獲取到 CPU 的使用權后,才可以執行指令。
也就是在任意時刻,只有一個線程占用 CPU,處于運行的狀態。
多線程并發運行實際上是指多個線程輪流獲取 CPU 使用權,分別執行各自的任務。
線程的調度由 JVM 負責,線程的調度是按照特定的機制為多個線程分配 CPU 的使用權。
線程調度模型分為兩類:分時調度模型和搶占式調度模型。
分時調度模型
分時調度模型是讓所有線程輪流獲取 CPU 使用權,并且平均分配每個線程占用 CPU 的時間片。
搶占式調度模型
JVM 采用的是搶占式調度模型,根據優先級來選擇占用CPU,優先級越高線程被持行的幾率就越大,如果線程的優先級都一樣,那就隨機選擇一個線程,并讓該線程占用 CPU。
也就是如果我們同時啟動多個線程,并不能保證它們能輪流獲取到均等的時間片。
如果我們的程序想干預線程的調度過程,最簡單的辦法就是給每個線程設定一個優先級。
6.線程創建的四種方式
????1)繼承Thread類創建線程
????2)實現Runnable接口創建線程
????3)使用Callable和Future創建線程
????4)使用線程池例如用Executor框架
具體代碼也是非常簡單,僅僅貼出Future的代碼:
public class MyTask ?implements Callable<Object>{ ? ?
? ? private String args1;
? ? private String args2;
? ? //構造函數,用來向task中傳遞任務的參數
? ? public ?MyTask(String args1,String args2) {
? ? ? ? this.args1=args1;
? ? ? ? this.args2=args2;
? ? }
? ? //任務執行的動作
? ? @Override
? ? public Object call() throws Exception {
? ? ? ??
? ? ? ? for(int i=0;i<100;i++){
? ? ? ? ? ? System.out.println(args1+args2+i);
? ? ? ? }
? ? ? ? return true;
? ? }
}
MyTask myTask = new MyTask("www", "www");
FutureTask<Object> futureTask = new FutureTask<>(myTask);
Thread thread = new Thread(futureTask);
thread.start();
//get()阻塞等待直到任務執行完成。
boolean result = (boolean) futureTask.get();
7.LockSupport
我們可以使用它來阻塞和喚醒線程,功能和wait,notify有些相似,但是LockSupport比起wait,notify功能更強大,也好用的多。
1)對比wait,notify
wait和notify都是Object中的方法,在調用這兩個方法前必須先獲得鎖對象。
當對象的等待隊列中有多個線程時,notify只能隨機選擇一個線程喚醒,無法喚醒指定的線程。
LockSupport的話,我們可以在任何場合使線程阻塞,同時也可以指定要喚醒的線程。
public class LockSupportTest {
?
? ? public static void main(String[] args) {
? ? ? ? Thread parkThread = new Thread(new ParkThread());
? ? ? ? parkThread.start();
? ? ? ? System.out.println("開始線程喚醒");
? ? ? ? LockSupport.unpark(parkThread);
? ? ? ? System.out.println("結束線程喚醒");
?
? ? }
?
? ? static class ParkThread implements Runnable{
?
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? System.out.println("開始線程阻塞");
? ? ? ? ? ? LockSupport.park();
? ? ? ? ? ? System.out.println("結束線程阻塞");
? ? ? ? }
? ? }
}
可指定線程阻塞和喚醒。摘學于:https://www.cnblogs.com/takumicx/p/9328459.html
8.CountDownLatch
CountDownLatch開啟一組線程,讓每個線程去持行自己的任務,等所有線程都持行完畢后,再向下持行代碼。比如我們開啟一組線程去同步服務器的數據有 播放記錄,下載記錄,收藏記錄等等用戶信息。
CountDownLatch latch = new CountDownLatch(5);
? ? ? ? for (int i =0;i<5;i++){
? ? ? ? ? ? new Thread(new MyTask(latch,i*1000,i)).start();
? ? ? ? }
? ? ? ? try {
? ? ? ? ? ? latch.await();
? ? ? ? ? ? Log.e("WXY--","All the tasks are over.");
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
public class MyTask implements Runnable {
? ? CountDownLatch latch;
? ? int delayTime; // Test
? ? int curThread;
? ? public MyTask(CountDownLatch latch,int delayTime,int curThread) {
? ? ? ? this.latch = latch;
? ? ? ? this.delayTime = delayTime;
? ? ? ? this.curThread = curThread;
? ? }
? ? @Override
? ? public void run() {
? ? ? ? try {
? ? ? ? ? ? Thread.sleep(delayTime);
? ? ? ? ? ? Log.e("WXY--","current task = " + curThread);
? ? ? ? ? ? latch.countDown();
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
這篇相對簡單主要是認識線程,了解線程的方法。介紹了兩個線程同步的類。下一篇將學習多個線程并發引入的鎖等。
由于本人記性不好學者忘著,所以才一字一句的記錄下來,若有錯誤或其他問題請評論區教訓我~。
————————————————
版權聲明:本文為CSDN博主「WangRain1」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/WangRain1/article/details/103765325
總結
以上是生活随笔為你收集整理的多线程并发 (二) 了解 Thread的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 六脉神剑(说一说六脉神剑的简介)
- 下一篇: 世界上最贵的东西是什么手表(世界上最贵的