一、什么是線程
線程是一個(gè)程序內(nèi)部的順序控制流。
線程和進(jìn)程
1.進(jìn)程:每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間,進(jìn)程切換開(kāi)銷(xiāo)大。
2.線程:輕量的進(jìn)程,同一個(gè)線程共享代碼和數(shù)據(jù)空間,每個(gè)線程有獨(dú)立運(yùn)行的棧和程序計(jì)數(shù)器(PC),線程切換的開(kāi)銷(xiāo)小。
3.在操作系統(tǒng)中能同時(shí)運(yùn)行多個(gè)任務(wù)(程序)。
4.在同一應(yīng)用程序有多個(gè)順序流同時(shí)執(zhí)行。
問(wèn)題:對(duì)于單核的CPU,處理多個(gè)任務(wù)時(shí)。并不會(huì)同時(shí)真正的運(yùn)兩個(gè)以上的程序。那么它是怎樣做到處理多任務(wù)的呢?
解答:實(shí)際上是操作系統(tǒng)負(fù)責(zé)對(duì)處理器就是CPU等資源進(jìn)行分配組織和管理。實(shí)際上每一時(shí)刻只能做一件事,或者運(yùn)行某一個(gè)程序。由于在操作系統(tǒng)的管理下,CPU的處理功能被以非常小的時(shí)間間隔進(jìn)行劃分、進(jìn)行交替,每一個(gè)小的時(shí)間間隔我們稱(chēng)為時(shí)間片。當(dāng)時(shí)間片到期之后將去執(zhí)行下一個(gè)程序。由于交替的速度非常的快,這樣就給人一種同時(shí)運(yùn)行多個(gè)應(yīng)用程序或者同時(shí)做多件事的一種感覺(jué)。這種情況我們稱(chēng)為并發(fā)執(zhí)行。也就是假的,模擬的,通過(guò)快速的交替,表現(xiàn)成在同時(shí)做多件事。
并行執(zhí)行才是真正意義上的同一時(shí)刻做多個(gè)事情,同一個(gè)瞬間運(yùn)行不同的應(yīng)用程序。這樣就要求有多個(gè)CPU。支持程序并發(fā)運(yùn)行的操作系統(tǒng)我們稱(chēng)為多任務(wù)的操作系統(tǒng)或者說(shuō)是多進(jìn)程的操作系統(tǒng)。
并發(fā)性的工作原理還可以應(yīng)用在更高的層面上,我們可以在一個(gè)應(yīng)用程序的內(nèi)部,把它要完成的任務(wù)分解為多個(gè)更小的子任務(wù)以多條齊頭并進(jìn)的線索來(lái)并發(fā)的執(zhí)行,這種就稱(chēng)為線程或多線程。
二、線程的概念模型
Java語(yǔ)言實(shí)現(xiàn)或支持多線程的工作方式。
一個(gè)線程必須具備以下三方面的要素才能正常的工作。
1.虛擬的CPU,由java.lang.Thread類(lèi)封裝和虛擬。
2.CPU所執(zhí)行的代碼,傳遞給Thread類(lèi)的對(duì)象。
3.CPU所處理的數(shù)據(jù),傳遞給Thread類(lèi)的對(duì)象。
三、創(chuàng)建線程
第一種方式:使用Runnable接口創(chuàng)建線程
1.Java的線程是通過(guò)java.lang.Thread類(lèi)來(lái)實(shí)現(xiàn)。
2.每個(gè)線程都是通過(guò)某個(gè)特定的Thread對(duì)象所對(duì)應(yīng)的run()方法來(lái)完成其操作。方法run()稱(chēng)為線程體。
例1:
public class Thread1{
public static void main(String[] args){
Runner1 r=new Runner1();//2.創(chuàng)建實(shí)現(xiàn)Runnable接口的對(duì)象
Thread t=new Thread(r); //3.創(chuàng)建一個(gè)Thread類(lèi)的對(duì)象
t.start(); //4.啟動(dòng)線程
}
}
class Runner1 implements Runnable{//1.Runner1實(shí)現(xiàn)Runnable接口
public void run(){
for(int i=1;i<20;i++){
System.out.println(i);
}
}
}
程序運(yùn)行的結(jié)果是輸出1到19個(gè)數(shù)字。與在main()方法中直接寫(xiě)for循環(huán)是一樣的效果。
但實(shí)現(xiàn)的原理不一樣。
使用線程輸出的有兩個(gè)線程(main()主線程和t子線程),不使用只有一個(gè)線程。
創(chuàng)建線程的三個(gè)步驟
1.定義一個(gè)類(lèi)實(shí)現(xiàn)Runnable接口,重寫(xiě)接口中的run()方法。在run()方法中加入具體的任務(wù)代碼或處理邏輯。
2.創(chuàng)建Runnable接口實(shí)現(xiàn)類(lèi)的對(duì)象。
3.創(chuàng)建一個(gè)Thread類(lèi)的對(duì)象,需要封裝前面Runnable接口實(shí)現(xiàn)類(lèi)的對(duì)象。(接口可以實(shí)現(xiàn)多繼承)
4.調(diào)用Thread對(duì)象的start()方法,啟動(dòng)線程
第二種方式:直接繼承Thread類(lèi)創(chuàng)建對(duì)象
例2:
public class Thread2{
public static void main(String[] args){
Thread t=new Runner2();
t.start();
}
}
class Runner2 extends Thread{
public void run(){
for(int i=1;i<20;i++){
String s=Thread.currentThread().getName();
System.out.println(s+":"+i);
}
}
}
1.首先定義一個(gè)類(lèi)去繼承Thread父類(lèi),此時(shí)Runner2類(lèi)并沒(méi)有直接的實(shí)現(xiàn)Runnable接口,但其實(shí)Thread類(lèi)在JDK中已經(jīng)實(shí)現(xiàn)了Runnable接口。這樣就Runner2類(lèi)間接的實(shí)現(xiàn)了Runnable接口。重寫(xiě)父類(lèi)中的run()方法。在run()方法中加入具體的任務(wù)代碼或處理邏輯。
2.直接創(chuàng)建一個(gè)Runner2類(lèi)的對(duì)象,也可以利用多態(tài)性,變量t聲明為父類(lèi)的類(lèi)型。
3.線程t啟動(dòng),隱含的調(diào)用run()方法。
比較兩種方式:
a.使用Runnable接口創(chuàng)建線程
1.可以將CPU,代碼和數(shù)據(jù)分開(kāi),形成清晰的模型
2.線程體run()方法所在的類(lèi)可以從其它類(lèi)中繼承一些有用的屬性和方法
3.有利于保持程序的設(shè)計(jì)風(fēng)格一致
b.直接繼承Thread類(lèi)創(chuàng)建對(duì)象
1.Thread子類(lèi)無(wú)法再?gòu)钠渌?lèi)繼承(java語(yǔ)言的單繼承)。
2.編寫(xiě)簡(jiǎn)單,run()方法的當(dāng)前對(duì)象就是線程對(duì)象,可直接操作。