多線程-thread
Thread類和Runnable接口都在java.lang包中。
內(nèi)容:
1.多線程的基本概念
2.創(chuàng)建線程
3.實(shí)例-龜兔賽跑
4.線程之間的數(shù)據(jù)交流
5.線程調(diào)度
6.線程的基本控制(暫放,這里的實(shí)例是賣票那個(gè)例子)
7.線程同步(synchronized 標(biāo)記)
實(shí)現(xiàn)線程同步化需要使用synchronized 關(guān)鍵字
8.線程死鎖
1.多線程的概念
java 語(yǔ)言支持多線程技術(shù)。(multithreaded programing)
為什么要有多線程?
是在編程時(shí),會(huì)遇到多個(gè)用戶的請(qǐng)求,為了更好地響應(yīng)多個(gè)用戶的請(qǐng)求,并及時(shí)作出響應(yīng)。
(1)線程的概念
先介紹進(jìn)程和多進(jìn)程的概念,再介紹線程的概念。
1.1 什么叫做進(jìn)程?進(jìn)程是程序的一次動(dòng)態(tài)的執(zhí)行過(guò)程,經(jīng)歷了代碼開(kāi)始到執(zhí)行完畢的一個(gè)完整過(guò)程。多進(jìn)程操作系統(tǒng)能并發(fā)運(yùn)行多個(gè)進(jìn)程。
并發(fā)運(yùn)行實(shí)際上是交叉運(yùn)行。
因?yàn)橛?jì)算機(jī)某個(gè)時(shí)刻只能執(zhí)行一個(gè)任務(wù),
因此多進(jìn)程是指多個(gè)進(jìn)程交叉運(yùn)行,給人的感覺(jué)好像是同時(shí)運(yùn)行的。
什么叫線程?
線程是由進(jìn)程派發(fā)的,是比進(jìn)程更小的執(zhí)行單位。
多線程是指在單個(gè)程序(或進(jìn)程)中同時(shí)運(yùn)行多個(gè)線程完成不同的工作,這些線程可以同時(shí)存在、同時(shí)運(yùn)行,形成多條執(zhí)行線程。如下圖所示。
線程與進(jìn)程也有共同點(diǎn)和不同點(diǎn)之處。
共同點(diǎn):實(shí)現(xiàn)并發(fā)的一個(gè)基本單位,都要系統(tǒng)進(jìn)行調(diào)度以提高運(yùn)行效率。
不同點(diǎn):(1)線程是比進(jìn)程更小的執(zhí)行單位;
? ? ? ? ? ? ? (2)每個(gè)進(jìn)程都有一個(gè)專門的內(nèi)存區(qū)域,而線程卻共享內(nèi)存單元,線程是通過(guò)共享的內(nèi)存單元來(lái)實(shí)現(xiàn)數(shù)據(jù)交換、實(shí)時(shí)通信等操作。
(2)線程的狀態(tài)與生命周期
思考:當(dāng)線程的run()方法運(yùn)行完成,線程就轉(zhuǎn)到(死亡)狀態(tài)。
用程序進(jìn)行解釋線程的狀態(tài)與生命周期:
(1)主類中創(chuàng)建新線程對(duì)象,并且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)建新線程的實(shí)例化對(duì)象。1.(新建狀態(tài))
? ? ? ? th.start();//2.(就緒狀態(tài))
}
}
在一個(gè)系統(tǒng)中,任何時(shí)刻最多只有一個(gè)運(yùn)行態(tài)線程。
sleep(),yield(),wait(),join()四個(gè)方法的定義和區(qū)別?
2.創(chuàng)建線程
(1)創(chuàng)建線程有兩種方式:
? ? ? 1.通過(guò)繼承Thread類創(chuàng)建線程。(喜歡用這個(gè))
? ? ? ? 2.通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建線程。
是否在任何情況下都能用任何一種方式創(chuàng)建線程?
注:(1)Thread類和Runnable接口都在java.lang包中。
? ? ? ? ? (2)? 創(chuàng)建新線程必須有run()方法。
解釋:
Runnable接口:在這個(gè)接口中只定義了run()方法,這個(gè)方法是線程執(zhí)行的主體,在線程獲取CPY時(shí)間運(yùn)行時(shí),就是自動(dòng)執(zhí)行線程的run()方法,因此,新的線程要完成的任務(wù)流程應(yīng)該寫在run()方法中,所以創(chuàng)建新線程就必須有run()方法。創(chuàng)建線程時(shí),可以通過(guò)實(shí)現(xiàn)Runnable接口獲取run()方法。
Thread類:這個(gè)類是已經(jīng)實(shí)現(xiàn)了Runnable接口的類,在這個(gè)類中除了擁有run()方法,還提高了對(duì)線程操作的方法,因此可以通過(guò)繼承Thread類創(chuàng)建新線程。
1.通過(guò)繼承Thread類創(chuàng)建線程
Thread類格式:
pulic class Thread extends Object implements Runnable
實(shí)例:
ThreadTest.java
package Thread;
/*步驟:
(1)主類中創(chuàng)建新線程對(duì)象,并且start().
(2)在創(chuàng)建的非主類中繼承Thread類進(jìn)行寫方法run().
*/
//通過(guò)繼承Thread類創(chuàng)建新線程
class Thread1 extends Thread{
//3.那么就開(kāi)始執(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)建新線程對(duì)象
th.start(); //2.啟動(dòng)線程Thread1
for(int i=0;i<100;i++)
System.out.println("main在運(yùn)行");//4.執(zhí)行完線程后,執(zhí)行主線程中剩余的語(yǔ)句。
}
}
在這里有兩條執(zhí)行線索。由于線程的執(zhí)行速度取決于CPU時(shí)間及速度,因此線程的程序在不同的機(jī)器上運(yùn)行時(shí),線程交替占用CPU的情況可能不一樣。因此運(yùn)行結(jié)果也會(huì)出現(xiàn)一些差別。
2.通過(guò)實(shí)現(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ū)別,
就是把實(shí)現(xiàn)相關(guān)的創(chuàng)建的實(shí)例對(duì)象傳給了Thread,同時(shí)Thread又創(chuàng)建了對(duì)象。
*/
Thread2 th2 = new Thread2();
Thread t1 = new Thread(th2);
t1.start();
}
實(shí)例:
package Thread;
/*(1)通過(guò)Runnable接口創(chuàng)建線程,基本與繼承Thread類創(chuàng)建線程一致,就是在主類中有區(qū)別。
? *(2)與繼承Thread類創(chuàng)建線程的區(qū)別:
就是把實(shí)現(xiàn)相關(guān)的創(chuàng)建的實(shí)例對(duì)象傳給了Thread,同時(shí)Thread又創(chuàng)建了對(duì)象。
? *? ? 然后利用Thread的實(shí)例化對(duì)象啟動(dòng)線程。
? * */
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ū)別,
就是把實(shí)現(xiàn)相關(guān)的創(chuàng)建的實(shí)例對(duì)象傳給了Thread,同時(shí)Thread又創(chuàng)建了對(duì)象。
*/
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.實(shí)例-龜兔賽跑
4.線程之間的數(shù)據(jù)交流
同一個(gè)進(jìn)程中的幾個(gè)線程之間是可以 共享數(shù)據(jù)的。
例如:在龜兔賽跑中,可以通過(guò)獎(jiǎng)勵(lì)他們食物的方式激勵(lì)他們賽跑。
而食物就是烏龜和兔子共享的數(shù)據(jù)。
則共享數(shù)據(jù)有兩種方式:
1.通過(guò)內(nèi)類創(chuàng)建線程
2.通過(guò)構(gòu)造器傳遞參數(shù)
1.通過(guò)內(nèi)類創(chuàng)建線程
內(nèi)類的作用是可以直接訪問(wèn)外類的成員變量。即把共享數(shù)據(jù)定義為外類的成員變量,烏龜和兔子定義為內(nèi)類,這樣就可以實(shí)現(xiàn)兩個(gè)線程之間的數(shù)據(jù)交流了。
在本例子中,inrabtort類是包含了主類,兔子類,烏龜類。其中主類外面定義了food,作為外類的成員變量。
實(shí)例:
//把所有類都寫在主類里了,且成員變量定義在主類外面。????
內(nèi)類:把一個(gè)類(內(nèi)類)定義在另一個(gè)類(外類)中。
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+"個(gè)食物,還剩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+"個(gè)食物,還剩food="+food);
}
}
}
}
}
2.通過(guò)構(gòu)造器傳遞參數(shù)(比較好用,不出錯(cuò))
把共享數(shù)據(jù)寫在一個(gè)單獨(dú)的類里,然后通過(guò)構(gòu)造器傳遞參數(shù)給各個(gè)線程。也可以對(duì)公用數(shù)據(jù)定義方法,供各個(gè)線程使用。
在本例中,把food這個(gè)共享數(shù)據(jù)單獨(dú)放在一個(gè)類里,然后在兔子類和烏龜類里都定義了food fd;
然后并設(shè)置了構(gòu)造器進(jìn)行賦值,然后進(jìn)行了引用。也就是說(shuō)food類,烏龜類,兔子類,主類這四個(gè)是并列的。
實(shí)例:
package thread;
import thread.inrabtort.rabbit;
import thread.inrabtort.tortoise;
class food{
public int food =10; //共同訪問(wèn)的數(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+"個(gè)食物,還剩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+"個(gè)食物,還剩food="+fd.food);
}
}
}
}
5.線程調(diào)度
(1)為什么存在線程調(diào)度?
(2)優(yōu)先級(jí)
默認(rèn)優(yōu)先級(jí)是5,可以通過(guò)線程實(shí)例對(duì)象.setPriority(1-10之間的數(shù)字)進(jìn)行設(shè)置。
(3)sleep(),yield(),join()的介紹
? ?使用格式:Thread.sleep(1000);
7.線程同步(synchronized 標(biāo)記)
如果不會(huì),在eclipse里輸入syn+ alt +?進(jìn)行提示。
實(shí)現(xiàn)線程同步化需要使用synchronized 關(guān)鍵字。
保證任意時(shí)刻只有一個(gè)線程訪問(wèn)該共享數(shù)據(jù)。