1. 程式人生 > >[20]多執行緒(部分)

[20]多執行緒(部分)

一、執行緒和程序的概念

程序:正在執行的程式。應用程式在記憶體中開闢的空間。

執行緒:程序的執行單位。負責程式中程序的執行。每個程序至少有一個執行緒,允許程序中有多個執行緒。稱為多執行緒。此程式稱為多執行緒程式。

程式啟動了多執行緒,可以實現多功能程式同時進行。也稱併發

【例子】平常看到的多執行緒

360中我們可以在防毒的同時,進行垃圾清理。如果是單執行緒,就必須防毒後,再垃圾清理。

多執行緒,其實就是合理的分配cpu資源。電腦之所以能同時執行聊天,音樂,遊戲也是因為多執行緒。但如果多執行緒多了,比如我開了LOL,DOTA2,CF,DNF,WOW那麼,經常就能看到程式未響應,是因為CPU在執行任務的時候忙不過來,過度的分配了cpu的資源,也就是說執行緒如果多到一定程度,就會降低效能。

為什麼要用到多執行緒?

public class Demo{
    public static void main(String[] args){
        show();
        System.out.println("Hello World")
    }

    public static void show(){
        for(int x=1;x<=10000000;i++){
            System.out.prinltn(i);
        }
    }
}

如果此時我要先執行show(),在執行到Hello World就要有個漫長的時間。現實中有很多例子,就比如考試的時候,做數學題,第一題就是奧數思考題,做出來的人很稀少。如果把時間花在這上面,後面的題就來不及做了。

所以就跟以上程式一樣,如果遇到了迴圈而導致了在指定的任務上停留時間過長,無法執行剩餘程式碼。就需要用到多執行緒來執行其他程式碼。

二、執行緒的定義

定義執行緒目前學到的有兩種:

一、繼承Thread類:

1.子類繼承Thread類:可以直接用Thread類,但是裡面的Thread建構函式如果不傳值。那就是執行空程式碼。至於裡面傳的什麼值,到Runnable再細講,所以如果不重寫Thread的run方法,就會執行空程式碼。並不是我們想要的執行緒任務。

2.重寫run方法

3.建立子類物件:就是建立執行緒。Thread類是執行緒,那麼子類物件也是執行緒。

4.呼叫start方法,開啟並讓執行緒執行,同時jvm還會去執行run方法

【例子】

public class Thread1 extends Thread{
    @override
    public void run(){
        for(int i=0;i<=5;i++){
            System.out.println(Thread.currentThread.getName()+":"+i);
        }
    }
}

public class Demo{
    public static void main(String[] args){
        Thread1 t1=new Thread1();
        Thread1 t2=new Thread1();
        t1.start();
        t2.run();
    }
}

執行結果:

 main:       1
 Thread-0:1
 main:       2
 Thread-0:2
 main:       3
 main:       4 
 main:       5
 Thread-0:3
 Thread-0:4
 Thread-0:5

先建立t1和t2兩個執行緒物件,之後t1執行緒start()執行,且jvm呼叫run(),而t2.run()只是單純的呼叫函式,並沒有呼叫start()執行執行緒,所以此時t1的執行緒為主執行緒。

如果t2.run()和t.start()順序相反一下,那麼就會按照正常的執行順序。

主執行緒:jvm啟動後,必會有一個執行緒從main()執行,在main()結束。此執行緒成為主執行緒。

主執行緒內容先執行,因為主執行緒因為有main(),先佔用了cpu資源,先執行主執行緒。當然也可以設定優先順序。

static Thread currentThread() 
          返回對當前正在執行的執行緒物件的引用。
String getName() 
          返回該執行緒的名稱。

執行緒之間,互不影響

public class Thread1 extends Thread{
    @override
    public void run(){
        int[] arr=new int[3];
        System.out.println(arr[4]);
    }
}

public class Thread2 extends Thread{
    @override
    public void run(){
        System.out.println(Thread.currenThread.getName());
    }
}

public class Demo{
    public static void main(String[] args){
        Thread1 t1=new Thread1();
        Thread2 t2=new Thread2();

        t1.start();
        t2.start();
    }
}

Thread-1
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 4
    at com.Thread.Thread1.run(Thread1.java:8)

 二、實現Runnable介面:優於Thread

1.子類實現Runnble介面:避免了繼承Thread類的侷限性。

例如:360安全衛士需要多執行緒,繼承了Thread類,而360安全衛士也要繼承於父類“防毒軟體”,所以這時候需要實現介面。

public class Thread     extends Object implements Runnable

在API中,Thread類其實就是實現了Runnable介面,並且重寫run()

2.重寫run方法

3.建立Thread物件:因為Runnable是介面無法建立物件,其子類Thread類是執行緒物件。

4. 把Runnable作為引數傳遞給Thread的建構函式

5.呼叫start方法開啟執行緒

API文件中有這麼一段話:

設計該介面的目的是為希望在活動時執行程式碼的物件提供一個公共協議。例如,Thread 類實現了 Runnable。啟用的意思是說某個執行緒已啟動並且尚未停止。

Runnable 為非 Thread 子類的類提供了一種啟用方式。通過例項化某個 Thread 例項並將自身作為執行目標,就可以執行實現 Runnable 的類而無需建立 Thread 的子類。大多數情況下,如果只想重寫 run() 方法,而不重寫其他 Thread 方法,那麼應使用 Runnable 介面。這很重要,因為除非程式設計師打算修改或增強類的基本行為,否則不應為該類建立子類。

以上那段話的意思,按本人理解差不多就是:

Runnable介面的目的就是為那些正在執行程式碼的物件提供了一個公共協議:某個執行緒已經啟動,並且未停止。

Thread為Runnable的子類,但是有侷限性。那麼如果不是Thread子類的類呢,如果想要啟用此公共協議,那麼只要先實現Runnable例項化Thread的類將自身作為執行目標(作為引數),就可以不建立Thread子類了。

大多數情況下,如果只是重寫run方法(只是重寫了執行緒功能而已),不需要重寫其他Thread類方法,那麼就該實現Runnable介面,這很重要,因為除非程式設計師打算(修改或增強Thread類的基本行為),否則不應該為Thread類派生子類。

簡而言之:就是本來如果不想為Thread類增強基本行為,只是想重寫run()方法。那麼就不應該繼承Thread類,而是去實現Runnable的介面。

把Runnable作為引數傳遞給Thread的建構函式

之前Thread沒說清楚的,放在這裡比較合適。

Thread類中有一個run方法,裡面寫的內容:如果target物件不為空,那麼就執行target的run方法。

那麼target是什麼呢?

原始碼裡寫著是Runnable類的引用變數,並且上面寫著what will be run,也就是說target就是執行的內容。

那麼target從哪來呢?在Thread的有參建構函式中,會傳入init方法中

所以,就是說我們先利用子類實現Runnble後,建立物件,利用多型傳參,並且將此物件通過init方法賦給Thread類中target。之後run方法呼叫的就是這個taget

 

【例子】

通過售貨機賣票如果要賣1000張票。

package com.ThreadTest;

public class Ticket implements Runnable{
	
	private boolean state=true;
	private int ticket;
	
	public void setTicket(int ticket) {
		this.ticket = ticket;
	}
	
	public int getTicket() {
		return ticket;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(state) {
			if (ticket>0) {
				System.out.println(Thread.currentThread().getName()+":"+ticket--);
			}else {
				state=false;
			}
		}
	}
}

package com.ThreadTest;

public class Demo {
	public static void main(String[] args) {
		Ticket t=new Ticket();
		t.setTicket(1000);
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		Thread t3=new Thread(t);
		Thread t4=new Thread(t);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

【未完待續】