53.圖書管理系統設計,路由,虛擬環境
阿新 • • 發佈:2022-04-06
執行緒
要想學習多執行緒,就得先知道什麼是執行緒,要想知道執行緒,就得先知道什麼是程序。
- 程序:
是指正在執行的程式,是系統進行資源分配和呼叫的獨立單位,每一個程序都有它自己的記憶體空間和資源。
通過工作管理員看- 執行緒:
是程序的單個順序控制流,或者就是說是一個單獨執行的路徑
如果一個程序只有一條執行路徑,稱之為單執行緒
如果一個程序有多條執行路徑,稱之為多執行緒
執行緒是包含在程序中的。
舉例:掃雷,360防毒軟體,百度網盤
何為序列,並行,併發?
- 1、序列,是指一個程式中所有的任務都是按照先後順序執行的,在前一個任務還沒有處理完的情況下,是不會進行處理下一個任務的。
舉例:理髮店只有一個理髮師,很多人去理髮,就需要排隊,就有先後順序,先等前面的人理完髮,再輪到後面的人。- 2、並行,是指將任務分給不同的處理器去畜欄裡,每一個處理器中的任務再進行序列處理
舉例:火車站上有很多賣票視窗,多個視窗同時賣票,但是呢,針對於某一個視窗來說,是一個接著一個去處理的。- 3、併發,是指一個現象,併發需要處理器的支援,比如在處理一個任務的時候,作業系統可以呼叫資源去處理其他的任務,這個任務並行還是序列都可以
無論是序列還是並行,都需要處理支援併發。
舉例:假設喝水是一個任務,每個火車站售票員,他再售票的同時也能喝水,這就表示支援併發
思考?JVM啟動的時候是單執行緒還是多執行緒呢?多執行緒
- JVM啟動的時候,相當於啟動了一個程式,就是啟動了一個程序
其中包含了主執行緒,垃圾回收執行緒。
在啟動JVM的時候,最低的要求是需要啟動兩個執行緒,所以JVM啟動的時候是多執行緒程式。
建立執行緒的第一種方式:繼承Thread類,重寫run方法
- 1、建立一個自定義類繼承Thread類
- 2、這個繼承的類要重寫run方法
1)為什麼要重寫run方法?
2)重寫run方法後如何使用?- 3、根據這個類建立執行緒物件
- 4、啟動執行緒
面試題:執行緒呼叫start()和run()方法的區別?
- 單純地呼叫run方法僅僅表示的是第一個物件呼叫普通的方法,所以這裡的執行還是按照自上而下順序執行,所以這裡依舊是單執行緒程式
要想看到多執行緒程式的執行效果,就必須換一種方式啟動執行緒。start()
run方法中僅僅是封裝了被執行緒執行的邏輯程式碼,但是直接物件呼叫run方法於普通的方法呼叫沒有任何區別
start()方法呼叫,首先做的是啟動一個執行緒,然後再由JVM去呼叫該執行緒物件中的run()方法
模擬多執行緒環境
- 要想模擬多執行緒環境,就得建立多個執行緒物件,然後同時啟動
模擬多執行緒環境,至少建立2個及以上的執行緒物件
注意事項
- 1、啟動執行緒呼叫的是start()方法
- 2、執行緒的呼叫start()方法先後順序與今後真正執行的順序沒有影響
執行緒的邏輯程式碼
package com.shujia.wyh.day25;
public class MyThread1 extends Thread{
@Override
public void run() {
//寫的是執行緒要執行的邏輯程式碼
// System.out.println("數加真好!");
//一般情況下,執行緒執行的邏輯程式碼都是比較耗時並且複雜的,為了模擬這裡耗時複雜,我使用迴圈列印來代替
for(int i=1;i<=300;i++){
System.out.println(i);
}
}
}
Thread類的基本獲取和設定方法
1.設定名字(兩種方法)
- 1、public final void setName(String name)
- 2、通過構造方法給執行緒起名字Thread(String name)
獲取名字
- 如何獲取一個執行緒的名字呢?
public final String getName()
public class MyThreadDemo3 {
public static void main(String[] args) {
// MyThread2 t1 = new MyThread2(); //Thread-0
// MyThread2 t2 = new MyThread2(); //Thread-1
MyThread2 t1 = new MyThread2("李毅");
MyThread2 t2 = new MyThread2("小虎");
// t1.setName("李毅");
// t2.setName("小虎");
//啟動執行緒
t1.start();
t2.start();
}
}
執行緒排程問題
問題引入
- 假如我們的計算機只有一個 CPU,那麼 CPU 在某一個時刻只能執行一條指令,執行緒只有得到 CPU時間片,也就是使用權,
才可以執行指令。那麼Java是如何對執行緒進行呼叫的呢?
執行緒的兩種排程模型(Java屬於搶佔式排程模型)
- 1、分時排程模型 所有執行緒輪流使用 CPU 的使用權,平均分配每個執行緒佔用 CPU 的時間片
- 2、搶佔式排程模型 優先讓優先順序高的執行緒使用 CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個,
Java使用的是搶佔式排程模型。***
獲取執行緒中的優先順序方法:
public final int getPriority() 返回此執行緒的優先順序。
設定執行緒優先順序的方法:
- public final void setPriority(int newPriority) 更改此執行緒的優先順序。
最大和最小優先順序
public final static int MIN_PRIORITY = 1; 執行緒可以擁有的最小的優先順序
public final static int MAX_PRIORITY = 10; 執行緒可以擁有的最大的優先順序
注意事項:
- 1、執行緒的預設優先順序是5
- 2、設定優先順序的時候,範圍是1-10
- 3、執行緒的優先順序越高僅僅表示的是獲取CPU時間片的機率會高一些,並不能保證一定會先執行。
程式碼如下:
public class ThreadPriorityDemo {
public static void main(String[] args) {
MyPriorityThread p1 = new MyPriorityThread();
MyPriorityThread p2 = new MyPriorityThread();
MyPriorityThread p3 = new MyPriorityThread();
//獲取執行緒優先順序
// int py1 = p1.getPriority();
// System.out.println(py1); 5
// int py2 = p2.getPriority();
// System.out.println(py2); 5
// int py3 = p3.getPriority();
// System.out.println(py3); 5
//可以設定執行緒的優先順序
p1.setPriority(10);
p2.setPriority(5);
p3.setPriority(1);
//設定各個執行緒的名字
p1.setName("小花");
p2.setName("小黑");
p3.setName("小白");
p1.start();
p2.start();
p3.start();
}
}
執行緒加入 public final void join()
- public final void join()
執行緒物件呼叫該方法的時候,目的是讓呼叫該方法的當前執行緒先執行完,執行完畢後,再讓其他執行緒執行
其他沒有呼叫join方法的執行緒,他們之間還是會搶CPU執行權的。
注意事項:
- join方法的呼叫,必須是緊跟著當前執行緒start()方法後呼叫,否則不起作用。
public class JoinDemo {
public static void main(String[] args) {
MyJoinThread jd1 = new MyJoinThread();
MyJoinThread jd2 = new MyJoinThread();
MyJoinThread jd3 = new MyJoinThread();
//給各個執行緒取名字
jd1.setName("無敵小可愛");
jd2.setName("無敵菠蘿怪");
jd3.setName("無敵香蕉π");
//注意:join 方法的呼叫,必須是緊跟著當前執行緒start()方法後呼叫的,否則不起作用;
//啟動各個執行緒
// try {
// jd1.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
jd1.start();
try {
jd1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
jd2.start();
jd3.start();
}
}
執行緒禮讓 public static void yield()
- 禮讓執行緒的目的是暫停當前正在執行的執行緒,並讓其他執行緒執行,
它的作用實際上是為了讓執行緒之間看起來更加和諧,它並不能保證多個執行緒之間一人一次。
public class MyYieldDemo {
public static void main(String[] args) {
//建立多執行緒環境
MyYieldThread t1 = new MyYieldThread();
MyYieldThread t2 = new MyYieldThread();
MyYieldThread t3 = new MyYieldThread();
//給執行緒設定名字
t1.setName("小白");
t2.setName("小黑");
t3.setName("小紅");
t1.start();
t2.start();
t3.start();
}
}
後臺執行緒(守護執行緒) public final void setDaemon(boolean on)
執行緒分類
- 使用者執行緒:我們在學習執行緒之前,執行起來的一個一個程式中的執行緒都是使用者執行緒
- 守護執行緒:所謂的守護執行緒,指的是程式執行的時候,在後臺提供了一個通用的服務執行緒,比如說垃圾回收執行緒,他就是一個守護執行緒。
這種執行緒不一定是要存在的,但是可能程式會出問題。只要程式存在使用者執行緒,程式就不會停止
守護執行緒的設定:
- public final void setDaemon(boolean on)
注意事項:
- 1、守護執行緒必須在啟動之前進行設定
public class ThreadDaemonDemo {
public static void main(String[] args) {
//模擬多執行緒環境
MyDaemonThread dd1 = new MyDaemonThread();
MyDaemonThread dd2 = new MyDaemonThread();
MyDaemonThread dd3 = new MyDaemonThread();
//給執行緒付名字
// dd1.setName("草莓");
// dd1.setDaemon(true);
dd2.setName("西瓜");
dd2.setDaemon(true);
dd3.setName("菠蘿");
dd3.setDaemon(true);
//當所有執行緒都為守護程序時。守護程序會馬上死亡,因為它沒有需要守護的東西
//當草莓為執行緒,其他倆是守護執行緒時,如果草莓程序結束,那麼,西瓜和菠蘿也會很快調亡
//啟動執行緒
// dd1.start();
dd2.start();
dd3.start();
}
}
休眠執行緒
- 模擬真實環境,讓執行緒進入休眠狀態
package com.bigdat.java.day26;
public class MySleepThread extends Thread{
@Override
public void run() {
for (int i = 0; i <= 300; i++) {
//需求:每列印一次,間隔 1000 毫秒在列印下一個
//public static void sleep(long millis) throws InterruptedException
//史當前正在執行的執行緒已指定的毫秒數暫停
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+i);
}
}
}
package com.bigdat.java.day26;
/*
public static void sleep(long millis)
*/
public class ThreadSleepDemo {
public static void main(String[] args) {
//建立三個程序
MySleepThread st1 = new MySleepThread();
MySleepThread st2 = new MySleepThread();
MySleepThread st3 = new MySleepThread();
//給程序名字
st1.setName("視窗一");
st2.setName("視窗二");
st3.setName("視窗三");
//啟動執行緒
st1.start();
st2.start();
st3.start();
}
}
中斷執行緒
- 在自定義類中定義每個執行緒睡眠5秒
public class MyStopTread extends Thread{
@Override
public void run() {
System.out.println("我開始睡覺。。。。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡醒了。。。");
}
}
- 然後再測試類中,將正在睡眠的執行緒喚醒,即打斷執行緒的休眠時間
兩種方法
- public final void stop()
強制打斷睡眠,程式停止,這個方法不太好,這個方法已經被棄用
- public void interrupt()/打斷睡眠,提示打斷睡眠的錯誤,run方法中後面的程式碼繼續執行,直到執行完畢
public class ThreadStopDemo {
public static void main(String[] args) {
MyStopTread myStopTread = new MyStopTread();
myStopTread.start();
try {
Thread.sleep(2000);
// myStopTread.stop(); //強制打斷睡眠,程式停止,這個方法不太好,這個方法已經被棄用
myStopTread.interrupt(); //打斷睡眠,提示打斷睡眠的錯誤,run方法中後面的程式碼繼續執行,直到執行完畢。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
多執行緒的第二種開啟方式-runnable
多執行緒的實現方式2:實現Runnable介面,實現run方法
- 1、自定義一個類實現Runnable介面
- 2、實現run方法
- 3、建立實現Runnable介面類對應的物件
- 4、藉助Thread類建立執行緒物件,將自定義類作為構造方法的引數傳入
主方法程式碼:
public class MyRunnableDemo1 {
public static void main(String[] args) {
//建立實現Runnable介面類對應的物件
MyRunnable1 myRunnable1 = new MyRunnable1();
//建立多個執行緒物件
// Thread t1 = new Thread(myRunnable1);
// Thread t2 = new Thread(myRunnable1);
//Thread(Runnable target, String name)
//分配一個新的 Thread物件。
Thread t1 = new Thread(myRunnable1,"小白");
Thread t2 = new Thread(myRunnable1,"小黑");
//給執行緒起名字
// t1.setName("李毅");
// t2.setName("小虎");
//啟動執行緒
t1.start();
t2.start();
}
}
自定義類程式碼
public class MyRunnable1 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 300; i++) {
//由於Runnable介面沒有getName()方法,所以在這裡無法直接呼叫獲取執行緒的名字
//間接呼叫,
//Thread類中有一個靜態的方法
// public static Thread currentThread()返回對當前正在執行的執行緒物件的引用。
// System.out.println(getName() + ":" + i);
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}