多線程(Thread)
*進程
*線程(例:FlashGet,迅雷)
*多線程存在的意義
*線程的建立方式
*多線程的特性
進程:是一個正在執行中的程式。
每一個進程執行都有一個執行的順序,該
順序就是一個執行路徑(情景),或者叫一個控制
單元。
線程:就是進程中的一個獨立的控制單元,線程在
控制著進程的執行。
一個進程中至少有一個線程。
java JVM 啟動的時候會有一個進程即Java.exe
該進程中至少有一個線程來負責java程式的執行
而且這個線程啟動並執行代碼存在於main方法中。
該線程稱之為主線程。
擴充:其實更細節說明JVM,JVM啟動不止一個線程
,還有負責記憶體回收機制的線程。
1.如何在自訂的代碼中,自訂一個線程呢?
通過對API的尋找,java已經提供了對線程這類 事
物的描述,就是Thread類。
建立線程的第一種方式:繼承Thread類
步驟:
1、定義類繼承Thread;
2、複寫Thread類中的run方法
目的:將自訂的代碼儲存在run方法中
,讓線程運行。
3、調用線程的start 方法,該方法有兩個作用:
一、啟動線程;二、調用run方法
例:
class Demo extends Thread
{
//覆蓋Run方法
public void run(){
syso("demo run");
}
}
class ThreadDemo{
public static void main(String[]
args){
Demo d=new Demo();//建立好一
個線程
//d.start();
d.run();
********面試******
RUN():僅僅是對象調用方法。而線程創
建了,並沒有運行。
Start():開啟線程並執行該線程的run方
法。
}
}
發現運行結果每一次都不同。
因為多個程式都在擷取CPU的執行權。CPU執行到誰
,誰就執行。
明確一點,在某一時刻,只能有一個程式在運行。
(多核CPU除外)
CPU在做著快速的切換,以達到看上去是同時運行
的效果。
我們可以形象把多線程的運行行為在互相搶奪CPU
的執行權。
這就是多線程的一個特性:隨機性。
為什麼要覆蓋Run方法呢?
Thread類用於描述線程。
該類就定義了一個功能,用於儲存線程要啟動並執行代
碼,該儲存功能就是run方法。
也就是說Thread類中的run方法,用於儲存線程要
啟動並執行代碼。
例子:
建立兩個線程,和主線程交替運行。
class Test extends Thread
{
public void run(){
for(int i=0;i<60;i++){
syso("test run"+x);
}
}
}
class ThreadTest{
public static void main(String []
args){
Test t1=new Test();
Test t2=new Test();
t1.start();
t2.start();
for(int j=0;j<60;j++){
syso("main...."+x);
}
}
}
建立線程方式一
繼承thread類
1、子類覆蓋父類中的run方法,將線程啟動並執行代碼
存放在RUN中。
2、建立子類對象的同時線程也被建立。
3、通過調用start方法開啟線程。
線程的第一種狀態:被建立,繼承Thread
線程的第二種狀態:凍結,時間到了就能運行
(sleep(time))
線程的第三種狀態:凍結,但是自己不能運行,(
wait等待自己不能返回(),notify喚醒());放棄
了執行資格。先從凍結上回到臨時狀態上,然後執
行。
線程的第四種狀態:消亡,stop(),run方法結束
。
線程的特殊狀態(臨時(阻塞)狀態):具備運行
資格,沒有執行權。
線程都有自己預設的名稱。
getName()方法//擷取線程名稱。設定線程名稱:
setName或者構造方法。
Thread.currentThread()方法 //擷取,當前線程
對象。時靜態。通用方式。
/*
需求:簡單的賣票程式。
多個視窗同時賣票。
*/
class Ticket extends Thread
{
//用靜態,一般不定義靜態,生命週期
太長
private static int tick=100;
public void run(){
while(true){
if(tick>0){
syso(Thread.currentThread
().getName+".....sale:"+tick--);
}
}
}
}
class TicketDemo
{
public static void main(String[]
args){
//***************第1。種
Ticket t1=new Ticket();
//Ticket t2=new Ticket();
//Ticket t3=new Ticket();
//Ticket t4=new Ticket();
//這樣會運行異常:無效的線程啟動異常
t1.start();
t1.start();
t1.start();
t1.start();
//************該如何解決呢?第二種建立方式
}
}
建立線程的第二種方法是:聲明實現Runnable介面
的類,該類然後實現run方法。然後可以分配該類
的執行個體,在建立Thread時作為一個參數來傳遞並啟
動。採用這種風格的同一個例子如下(JDK):
class PrimeRun implements Runnable{
long minPrime;
PrimeRun(long minPrime){
this.minPrime=minPrime;
}
public void run(){
}
}
建立線程的第二種方式:實現Runnable介面
步驟:
1.定義類實現Runnable介面
2.覆蓋Runnable介面中的run方法
將線程要啟動並執行代碼存放在該Run方法中
3.通過Thread類建立線程對象
4.將Runnable介面的子類對象作為實際參數傳遞給
Thread類的建構函式。
為什麼要將Runnable介面的子類對象傳遞
給Thread的建構函式。
因為,自訂的run方法所屬的對象是
Runnable介面的子類對象。所以要讓線程去指定指
定對象的run方法。就必須明確該run方法所屬的對
象。
5.調用Thread類的start方法開啟線程並調用
Runnable介面子類的run方法。
******面試****
實現方式和繼承方式有什麼區別?
實現方式好處:避免了單繼承的局限性。
在定義線程時,建議使用實現方式。
兩種方式區別:
繼承Thread:線程代碼存放在Thread子類run方法
中。
實現Runnable:線程代碼存在介面的字了的run方
法中。
-------------------------------------
售票簡單程式的另一種:
通過分析,發現,列印除了0,-1,-2等錯票
多線程的運行出現了安全問題。
問題的原因:
當多條語句在操作同一個線程共用資料時
,一個線程對多條語句只執行了一部分,還沒有執
行完另一個線程參與進來執行,導致共用資料的錯
誤。
解決辦法:
對多條操作共用資料的語句,只能讓一個
線程都執行完,在執行過程中,其他線程不可以參
與執行。
Java對於多線程的安全問題提供了專業的解決問題
。就是同步代碼塊如下:
synchronized(對象){
需要被同步的代碼
}
對象如同鎖。持有鎖的線程可以在同步中執行。沒
有持有鎖的線程即使擷取了CPU的執行權,也進不
去,因為沒有擷取鎖。
同步的前提:
1.必須要有兩個或者兩個以上的線程。
2.必須是多個線程使用同一個鎖。
*******************
必須保證同步中只能有一個線程在運行。
好處:解決了多線程的安全問題。
弊端:多個線程都需要判斷鎖,較為消耗資源。
一個是同步代碼塊;
一個是同步函數
******同步函數用的是哪個鎖?
函數需要被對象調用。那麼函數都有一個所屬對象
引用嗎,就是this。所以同步函數使用的鎖是this
。
如果同步函數被靜態函數修飾後,使用的鎖是什麼
呢?
不是this,因為靜態方法中的也不可以定義this。
靜態進記憶體時,記憶體中沒有本類對象,但是一定有
該類對應的位元組碼檔案對象。
類名.class 該對象的類型是Class。
靜態同步方法,使用的鎖是該方法所在類的位元組
碼檔案對象。也 就是:類名.class(唯一的)
class Ticket implements Runnable
{
private static int tick=100;
//Object對象
Object obj=new Object();
public void run(){
while(true){
//同步代碼塊(鎖旗標)
synchronized(obj){
if(tick>0){
syso(Thread.currentThread
().getName+".....sale:"+tick--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[]
args){
//這個建立的不是線程
Ticket t=new Ticket();
Ticket t1=new Ticket(t);
Ticket t2=new Ticket(t);
Ticket t3=new Ticket(t);
Ticket t4=new Ticket(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}