多線程-thread
Thread類和Runnable接口都在java.lang包中谱邪。
內(nèi)容:
1.多線程的基本概念
2.創(chuàng)建線程
3.實例-龜兔賽跑
4.線程之間的數(shù)據(jù)交流
5.線程調(diào)度
6.線程的基本控制(暫放橄仍,這里的實例是賣票那個例子)
7.線程同步(synchronized 標(biāo)記)
實現(xiàn)線程同步化需要使用synchronized 關(guān)鍵字
8.線程死鎖
1.多線程的概念
java 語言支持多線程技術(shù)。(multithreaded programing)
為什么要有多線程梁丘?
是在編程時,會遇到多個用戶的請求戴卜,為了更好地響應(yīng)多個用戶的請求捣域,并及時作出響應(yīng)授瘦。
(1)線程的概念
先介紹進(jìn)程和多進(jìn)程的概念,再介紹線程的概念竟宋。
1.1 什么叫做進(jìn)程?進(jìn)程是程序的一次動態(tài)的執(zhí)行過程形纺,經(jīng)歷了代碼開始到執(zhí)行完畢的一個完整過程丘侠。多進(jìn)程操作系統(tǒng)能并發(fā)運(yùn)行多個進(jìn)程。
并發(fā)運(yùn)行實際上是交叉運(yùn)行逐样。
因為計算機(jī)某個時刻只能執(zhí)行一個任務(wù)蜗字,
因此多進(jìn)程是指多個進(jìn)程交叉運(yùn)行打肝,給人的感覺好像是同時運(yùn)行的。
什么叫線程挪捕?
線程是由進(jìn)程派發(fā)的粗梭,是比進(jìn)程更小的執(zhí)行單位。
多線程是指在單個程序(或進(jìn)程)中同時運(yùn)行多個線程完成不同的工作,這些線程可以同時存在级零、同時運(yùn)行断医,形成多條執(zhí)行線程。如下圖所示奏纪。
線程與進(jìn)程也有共同點(diǎn)和不同點(diǎn)之處鉴嗤。
共同點(diǎn):實現(xiàn)并發(fā)的一個基本單位,都要系統(tǒng)進(jìn)行調(diào)度以提高運(yùn)行效率序调。
不同點(diǎn):(1)線程是比進(jìn)程更小的執(zhí)行單位醉锅;
? ? ? ? ? ? ? (2)每個進(jìn)程都有一個專門的內(nèi)存區(qū)域,而線程卻共享內(nèi)存單元发绢,線程是通過共享的內(nèi)存單元來實現(xiàn)數(shù)據(jù)交換硬耍、實時通信等操作。
(2)線程的狀態(tài)與生命周期
思考:當(dāng)線程的run()方法運(yùn)行完成边酒,線程就轉(zhuǎn)到(死亡)狀態(tài)经柴。
用程序進(jìn)行解釋線程的狀態(tài)與生命周期:
(1)主類中創(chuàng)建新線程對象,并且start().
(2)在創(chuàng)建的非主類中進(jìn)行寫方法run().
class Thread1 extends Thread{
//3.(運(yùn)行狀態(tài))? 一 一種是4.死亡狀態(tài)甚纲,一種是5.阻塞狀態(tài)(sleep(),wait(),join().)
? public void run(){
? ? ? ? 線程的任務(wù)
? }
}
public class Test1{
//主進(jìn)程 口锭,即main()方法。
? public static void main(String [] argps){
? ? ? ? Thread1 th = new Thread1(); //創(chuàng)建新線程的實例化對象介杆。1.(新建狀態(tài))
? ? ? ? th.start();//2.(就緒狀態(tài))
}
}
在一個系統(tǒng)中鹃操,任何時刻最多只有一個運(yùn)行態(tài)線程。
sleep(),yield(),wait(),join()四個方法的定義和區(qū)別春哨?
2.創(chuàng)建線程
(1)創(chuàng)建線程有兩種方式:
? ? ? 1.通過繼承Thread類創(chuàng)建線程荆隘。(喜歡用這個)
? ? ? ? 2.通過實現(xiàn)Runnable接口創(chuàng)建線程。
是否在任何情況下都能用任何一種方式創(chuàng)建線程赴背?
注:(1)Thread類和Runnable接口都在java.lang包中椰拒。
? ? ? ? ? (2)? 創(chuàng)建新線程必須有run()方法。
解釋:
Runnable接口:在這個接口中只定義了run()方法凰荚,這個方法是線程執(zhí)行的主體燃观,在線程獲取CPY時間運(yùn)行時,就是自動執(zhí)行線程的run()方法便瑟,因此缆毁,新的線程要完成的任務(wù)流程應(yīng)該寫在run()方法中,所以創(chuàng)建新線程就必須有run()方法到涂。創(chuàng)建線程時脊框,可以通過實現(xiàn)Runnable接口獲取run()方法颁督。
Thread類:這個類是已經(jīng)實現(xiàn)了Runnable接口的類,在這個類中除了擁有run()方法浇雹,還提高了對線程操作的方法沉御,因此可以通過繼承Thread類創(chuàng)建新線程。
1.通過繼承Thread類創(chuàng)建線程
Thread類格式:
pulic class Thread extends Object implements Runnable
實例:
ThreadTest.java
package Thread;
/*步驟:
(1)主類中創(chuàng)建新線程對象昭灵,并且start().
(2)在創(chuàng)建的非主類中繼承Thread類進(jìn)行寫方法run().
*/
//通過繼承Thread類創(chuàng)建新線程
class Thread1 extends Thread{
//3.那么就開始執(zhí)行run().[線程的任務(wù)寫在run()中]
public void run(){
for(int i=0;i<100;i++)
System.out.println("Thread1在運(yùn)行");
}
}
public class ThreadTest {
? public static void main(String[] args) {
Thread1 th = new Thread1(); //1.創(chuàng)建新線程對象
th.start(); //2.啟動線程Thread1
for(int i=0;i<100;i++)
System.out.println("main在運(yùn)行");//4.執(zhí)行完線程后吠裆,執(zhí)行主線程中剩余的語句。
}
}
在這里有兩條執(zhí)行線索虎锚。由于線程的執(zhí)行速度取決于CPU時間及速度硫痰,因此線程的程序在不同的機(jī)器上運(yùn)行時,線程交替占用CPU的情況可能不一樣窜护。因此運(yùn)行結(jié)果也會出現(xiàn)一些差別效斑。
2.通過實現(xiàn)Runnable接口創(chuàng)建線程
格式:
class Thread1 implements Runnable{
? ? ? ? ? public void run(){
? ? ? ? }
}
public class RunnableTest {
public static void main(String[] args) {
/*與繼承Thread類創(chuàng)建線程的區(qū)別,
就是把實現(xiàn)相關(guān)的創(chuàng)建的實例對象傳給了Thread柱徙,同時Thread又創(chuàng)建了對象缓屠。
*/
Thread2 th2 = new Thread2();
Thread t1 = new Thread(th2);
t1.start();
}
實例:
package Thread;
/*(1)通過Runnable接口創(chuàng)建線程,基本與繼承Thread類創(chuàng)建線程一致护侮,就是在主類中有區(qū)別敌完。
? *(2)與繼承Thread類創(chuàng)建線程的區(qū)別:
就是把實現(xiàn)相關(guān)的創(chuàng)建的實例對象傳給了Thread,同時Thread又創(chuàng)建了對象羊初。
? *? ? 然后利用Thread的實例化對象啟動線程滨溉。
? * */
class Thread2 implements Runnable{
public void run(){
for(int i=0;i<100;i++)
System.out.println("Thread1在運(yùn)行");
}
}
public class RunnableTest {
public static void main(String[] args) {
/*與繼承Thread類創(chuàng)建線程的區(qū)別,
就是把實現(xiàn)相關(guān)的創(chuàng)建的實例對象傳給了Thread长赞,同時Thread又創(chuàng)建了對象晦攒。
*/
Thread2 th2 = new Thread2();
Thread t1 = new Thread(th2);
t1.start();
for(int i=0;i<100;i++)
System.out.println("main在運(yùn)行");
}
}
3.實例-龜兔賽跑
4.線程之間的數(shù)據(jù)交流
同一個進(jìn)程中的幾個線程之間是可以 共享數(shù)據(jù)的。
例如:在龜兔賽跑中得哆,可以通過獎勵他們食物的方式激勵他們賽跑脯颜。
而食物就是烏龜和兔子共享的數(shù)據(jù)。
則共享數(shù)據(jù)有兩種方式:
1.通過內(nèi)類創(chuàng)建線程
2.通過構(gòu)造器傳遞參數(shù)
1.通過內(nèi)類創(chuàng)建線程
內(nèi)類的作用是可以直接訪問外類的成員變量贩据。即把共享數(shù)據(jù)定義為外類的成員變量栋操,烏龜和兔子定義為內(nèi)類,這樣就可以實現(xiàn)兩個線程之間的數(shù)據(jù)交流了饱亮。
在本例子中矾芙,inrabtort類是包含了主類,兔子類近上,烏龜類蠕啄。其中主類外面定義了food,作為外類的成員變量。
實例:
//把所有類都寫在主類里了歼跟,且成員變量定義在主類外面。????
內(nèi)類:把一個類(內(nèi)類)定義在另一個類(外類)中格遭。
inrabtort.java?
package thread;
public class inrabtort {
public int food = 10;
public static void main(String argps[]){
inrabtort tt = new inrabtort();
tt.go();
}
public void go(){
tortoise r1 = new tortoise();
rabbit r2 = new rabbit();
r1.start();
r2.start();
}
class tortoise extends Thread{
int i,f=0;
public void run(){
for(i=1;i<=10;i++){
System.out.println("烏龜跑到了"+i+"米處");
if(food>0){
food--;f++;
System.out.println("烏龜吃了第"+f+"個食物哈街,還剩food="+food);
}
}
}
}
class rabbit extends Thread{
int i,f=0;
public void run(){
for(i=1;i<=10;i++){
System.out.println("兔子跑到了"+i+"米處");
if(food>0){
food--;f++;
System.out.println("兔子吃了第"+f+"個食物,還剩food="+food);
}
}
}
}
}
2.通過構(gòu)造器傳遞參數(shù)(比較好用拒迅,不出錯)
把共享數(shù)據(jù)寫在一個單獨(dú)的類里骚秦,然后通過構(gòu)造器傳遞參數(shù)給各個線程。也可以對公用數(shù)據(jù)定義方法璧微,供各個線程使用作箍。
在本例中,把food這個共享數(shù)據(jù)單獨(dú)放在一個類里前硫,然后在兔子類和烏龜類里都定義了food fd;
然后并設(shè)置了構(gòu)造器進(jìn)行賦值胞得,然后進(jìn)行了引用。也就是說food類屹电,烏龜類阶剑,兔子類,主類這四個是并列的危号。
實例:
package thread;
import thread.inrabtort.rabbit;
import thread.inrabtort.tortoise;
class food{
public int food =10; //共同訪問的數(shù)據(jù)
}
public class structrabtort {
public static void main(String argps[]){
structrabtort? tt = new? structrabtort();
tt.go();
}
public void go(){
food f1 =new food();
tortoise1 r1 = new tortoise1(f1);
rabbit1 r2 = new rabbit1(f1);
r1.start();
r2.start();
}
}
class tortoise1 extends Thread{
int i;
int f=0;
food fd;
public tortoise1(food fd) {
this.fd = fd;
}
public void run(){
for(i=1;i<11;i++){
System.out.println("烏龜跑到了"+i+"米處");
if(fd.food>0){
fd.food--;f++;
System.out.println("烏龜吃了第"+f+"個食物牧愁,還剩food="+fd.food);
}
}
}
}
class rabbit1 extends Thread{
int i,f=0;
food fd;
public rabbit1(food fd) {
this.fd = fd;
}
public void run(){
for(i=1;i<11;i++){
System.out.println("兔子跑到了"+i+"米處");
if(fd.food>0){
fd.food--;f++;
System.out.println("兔子吃了第"+f+"個食物,還剩food="+fd.food);
}
}
}
}
5.線程調(diào)度
(1)為什么存在線程調(diào)度外莲?
(2)優(yōu)先級
默認(rèn)優(yōu)先級是5猪半,可以通過線程實例對象.setPriority(1-10之間的數(shù)字)進(jìn)行設(shè)置。
(3)sleep(),yield(),join()的介紹
? ?使用格式:Thread.sleep(1000)偷线;
7.線程同步(synchronized 標(biāo)記)
如果不會磨确,在eclipse里輸入syn+ alt +?進(jìn)行提示。
實現(xiàn)線程同步化需要使用synchronized 關(guān)鍵字淋昭。
保證任意時刻只有一個線程訪問該共享數(shù)據(jù)俐填。