執行緒是什麼
程序是對CPU的抽象,而執行緒更細化了程序的執行流程
先看一下這個圖
執行緒和程序的關係有
- 程序中就是執行緒在執行,所有(主)執行緒執行完了程序也就結束了
- 多個執行緒從1秒鐘是同時執行完成,從1納秒(或是更小的單位)看是排隊執行
- 在不斷執行的程序中(沒有IO操作),使用多個執行緒並沒有提高程序的執行效率,還有所降低
為什麼需要多執行緒
我第一次使用執行緒是在學習BIO和多執行緒進行通訊時
服務端每次接收到一個客戶端的請求就建立一個執行緒(雖然可以使用tomcat和Netty大大優化了BIO,但它們也使用了多執行緒)
就這個用法而言,難道不能用多程序代替多執行緒進行通訊嗎(不知道提出這個問題是不是太笨了)
我的理解是:
- 多個客戶端和服務端通訊時,服務端有很多資源是多個客戶端共用的
- 如果使用多程序,也就是執行很多個服務端,相同服務端就得存放相同的資源在電腦記憶體中,太浪費空間了(如果是為了防止故障存在多臺機器肯定就不浪費)
- 所以建立了執行緒這個概念去共用程序的資源
執行緒需要什麼
業務場景還是在通訊中
- 服務端執行緒需要在客戶端發訊息過來時及時響應,至於怎麼響應肯定就寫在自己的程式碼邏輯裡
- 所以執行緒需要一段可以執行的程式碼塊
- A發訊息,就得切換到與A通訊的執行緒,B發訊息,就得切換到與B通訊的執行緒,執行緒就得不停的切換
- 所以CPU得知道哪個執行緒和A通訊等,就得給執行緒個ID好辨認
總之執行緒還需要各種各樣我沒學過的東西。
下面根據自己的理解,實現一個簡單的使用者執行緒
使用者執行緒:如果裡面有一個執行緒執行不下去了(等待輸入、執行報錯、死迴圈),作業系統並不認識使用者執行緒,只認識使用者程序,會讓整個使用者程序執行不下去
實現一個簡單的使用者執行緒
執行緒的資料結構
MyThread.java
1 import java.lang.reflect.Constructor;
2 import java.lang.reflect.InvocationTargetException;
3 import java.lang.reflect.Method;
4
5 public class MyThread<T> {
6
7 private int Id;//區分各個執行緒的識別符號
8 private int runTime;//執行緒當前可以執行的時間片
9 private int totalTime; //執行緒總共需要執行的時間
10 private int priority;//執行緒的優先順序
11 //交給執行緒執行的程式碼塊,用一個物件引數替代
12 public T data;
13
14 public MyThread() {
15 }
16
17 public MyThread(int id, int totalTime,int priority, T data) {
18
19 this.Id = id;
20 // this.runTime = runTime;
21 this.totalTime = totalTime;
22 this.runTime = totalTime/2;//固定執行緒每次執行時間片是總時間的二分之一,方便測試
23 this.priority = priority;
24 this.data = data;
25 }
26
27 public void run(){
28
29 try {
30
31 System.out.println("執行緒id:"+Id);
32 System.out.println("剩餘時間片:" + runTime);
33 System.out.println("執行緒優先順序:"+priority);
34
35 //使用java的反射機制,執行程式碼塊的內容
36 Class<?> clazz = data.getClass();
37 Constructor constructor = clazz.getDeclaredConstructor();
38 Object object = constructor.newInstance();
39
40 Method method = clazz.getMethod("hello");
41 method.invoke(object);
42
43 //每執行一次,執行緒總時間減少
44 //執行的時間片不變
45 totalTime-=runTime;
46 // 判斷執行緒執行總時間是否快要結束了
47 runTime = Math.min(totalTime,runTime);
48
49 } catch (NoSuchMethodException e) {
50 e.printStackTrace();
51 } catch (InvocationTargetException e) {
52 e.printStackTrace();
53 } catch (InstantiationException e) {
54 e.printStackTrace();
55 } catch (IllegalAccessException e) {
56 e.printStackTrace();
57 }
58
59 }
60
61 public int getPriority() {
62 return priority;
63 }
64
65 public int getRunTime() {
66 return runTime;
67 }
68
69 public int getTotalTime() {
70 return totalTime;
71 }
72 }
寫一個類代替程式碼塊供執行緒執行
1 public class MyMethod {
2
3 public MyMethod() {
4
5 }
6
7 public void hello(){
8 System.out.println("hello");
9 }
10
11 }
測試執行緒的執行
1 import java.util.Comparator;
2 import java.util.PriorityQueue;
3 public class Test {
4
5 public static void main(String[] args) {
6
7 //由優先佇列進行執行緒的排程,即優先順序高的執行緒先執行,優先順序相同採用先入先出演算法
8 PriorityQueue<MyThread> priorityQueue = new PriorityQueue<>(new Comparator<MyThread>() {
9 @Override
10 public int compare(MyThread o1, MyThread o2) {
11 return o2.getPriority() - o1.getPriority();
12 }
13 });
14
15 MyMethod method = new MyMethod();
16 //建立三個執行緒
17 MyThread<MyMethod> myThread = new MyThread<>(1,3,5,method);
18 MyThread<MyMethod> myThread1 = new MyThread<>(2,3,5,method);
19 MyThread<MyMethod> myThread2 = new MyThread<>(3,2,10,method);
20 //執行緒進入佇列
21 priorityQueue.offer(myThread);
22 priorityQueue.offer(myThread1);
23 priorityQueue.offer(myThread2);
24
25 //在迴圈中不斷的執行執行緒
26
27 while (!priorityQueue.isEmpty()){
28
29 MyThread<MyMethod> myThreadRun = priorityQueue.poll();
30
31 myThreadRun.run();
32
33 //執行緒總時間不為0,進入佇列等待下次執行
34 if (myThreadRun.getTotalTime() != 0){
35 priorityQueue.offer(myThreadRun);
36 }
37 }
38
39 System.out.println("執行緒都執行完了,程序也就結束了");
40 }
41 }
執行結果
總結
- 這個使用者執行緒的例子實現的不好
- 例子中總共有四個執行緒,主執行緒擔任了一個排程執行緒的職責
- 在作業系統中,執行緒的執行
- 作業系統會不斷主動去詢問執行緒的時間片結束沒,一旦結束就將執行緒換下
- 多執行緒排隊執行出現競爭
- 一個執行緒執行一個列印命令,對應了CPU中的多條指令
- 如果指令沒執行完,切換到了其他執行緒,再換回來執行指令,就會出現一種競爭的情況