進程:是一個正在執行的程式 每個進程都有一個執行順序,該順序是一個執行路徑或叫一個控制單元, 線程:就是進程中的一個獨立的控制單元。 線程在控制著進程的執行 一個進程中可以有多個線程, 但是一個線程只能有一個進程 同一進程中的線程共用進程中的資源 多線程:如果需要一個程式中實現多段代碼交替運行,就需 ...
進程:是一個正在執行的程式 每個進程都有一個執行順序,該順序是一個執行路徑或叫一個控制單元,
線程:就是進程中的一個獨立的控制單元。 線程在控制著進程的執行
一個進程中可以有多個線程, 但是一個線程只能有一個進程 同一進程中的線程共用進程中的資源
多線程:如果需要一個程式中實現多段代碼交替運行,就需要產生多個線程,並指定每個線程上所要運行的代碼段,這就是多線程
當程式執行時自動會產生一個線程 ,主函數就是在這個線程上面運行的,當不再產生新的線程時,程式就是單線程的。
創建多線程的兩種 方法:繼承Thread 類主實現 Runnable介面
start()方法的作用:1,啟動線程 2,調用run()方法
多線程的特點:隨機性,誰先搶到資源誰執行,執行時間由cpu決定。
原因是多個線程都獲取CPU的執行權,但是CPU執行到誰誰就運行。
註:在某一個時刻,只有一個程式在運行,其他的都是在等待執行,程式在電腦里是併發執行的,
我們同時打開兩個程式他們的執行的所有的都是一樣的可能我們就會認為他們是在同一時間執行的那麼你錯了,所有的程式都是不能在同一時間執行的(多核除外)只是他們在一個很短的時間內由電腦完成了執行操作,但不是同一時間點
http://www.cnblogs.com/roucheng/p/javatimu.html
利用Thread類創建線程:
java線程是通過java.lang.Thread類來控制的一個Thread類代表一個線程,通過 Thread類和它定義的對象 我們可以得到當前線程對象,獲取某個線程的名稱,可以實現 控制線程暫停一段時間 等 功能。
public class ThreadDemo { public static void main(String []args) { new TestThread().run(); while(true) { System.out.println("main thread is running"); } } } class TestThread { public void run() { while(true) { System.out.println("Thread.currentThread().getName()is running"); } } } /* 何問起 hovertree.com */
我們使用Thread.currentThread()靜態函數獲得該代碼當前執行時對應的那個線程對象。得到這個線程對象 後雙調用 了線程對象 的getname()方法取出當前線程的名稱字元串
運行後我們會發現第一個代碼塊沒有運行而且是只運行了第二個代碼並且是無限迴圈。也就導致了代碼塊一處永遠沒有機會執行
public class ThreadDemo { pubnlic static void main(String[] args) { new TestThread().start(); while(ture) { System.out,println("main thread is running"); } } } class TestThread extends Thread//這裡讓TestThread類繼承Thread類 { public void run()//這裡調用了上面的.start函數因為繼承所以可以使用 { while(ture) { System.out.println("Thread.cuurrentThread().getName()is running"); } } } /* 何問起 hovertree.com */
這樣因為有了繼承這裡ThreadDemo類的全部特點也就讓TestThread類有瞭然而程式沒有直接 調用 TestThread類的對象
run方法也是調用了該 類對象 從Thread類繼承來的start方法也就讓兩個while迴圈達到了交替運行的效果。
在代碼段run()中,也可以通過 線程的靜態方法 Thread.currentThread()得到當前線程實例對象。得到當前線程對象後又調用了getName方法取出當前線程的名稱字元串
1.要將一段代碼在一個新的線程上運行,該代碼中必須在一個類的run函數中,並且run函數所在的類是Thread類中的子類。也就是要實現 多線程,必須要有一個繼承了Thread類的子類,子類要覆蓋Thread類中的run函數,在子類的run函數中調用 想在新線程上運行的程式代碼
2.啟動一個新的線程不是直接調用 Thread子類對象的run方法,也是調用Thread子類對象 的start(繼承的那個方法)方法Thread類對象start方法將產生一個新的線程,併在該線程上運行該Thread類對象 中的run方法,根據面向對象 的多態性,在該 線程上實際運行的是thread子類對象中的run方法
3.由於線程在run方法中,那麼該方法執行完後線程也就結束了,因些可以通過控制run方法中的迴圈條件來控制線程的終止
如果Thread類的子類沒有覆蓋run方法,編譯運行時有明顯的錯誤或異常麽?運行結果又是什麼呢?
程式調用 Thread類中的run方法而該方法什麼都 不做,所以新的線程剛創建那麼就結束了這樣的線程對我們來說沒有任何意義的
而且 直接 在程式 中寫new Thread().start();這樣的語句編譯和運行時有明顯的錯誤和異常麽?運行結果又是什麼?
這個與上面是一樣因為線程對象不是通過 Thread子類創建的而是通過 Thread類直接創建的,新的線程將直接 調用 Thread中的run()方法。
使用Runnable介面創建多線程:
這是一個構造方法Thread(Runnable target)構造方法.Runnable介面中只有一個方法run()方法。當使用Thread(Runnable target)方法創建線程時,需為該方法傳遞一個實現 了Runnable介面的類對象中的run()方法作為其運行代碼而不再調用Thread類中的run方法了
public class ThreadDemo { public static void main(String args[]) //new TestThread().start(); TestThread tt=new TestThread();//創建TestThread類的一個實例 Thread t= new TestThread();//創建一個Thread類的實例 t.start();//使線程進入Runnable狀態 while(true) { System.out.println("main thread is running"); } } } class TestThread implements Runnable//extends Thread { public void run()//線程的代碼段,當執行start()里,線程開始執行 { while(true) { System.out,println("Thread.currentThread().getName() is running"); } } } /* 何問起 hovertree.com */
兩種實現 多線程 方式的對比分析:
實現Runnable介面相對 於繼承Thread類來說的好處
1.適合多個相同程式代碼的線程去處理同一個資源的情況,把虛擬CPU(線程)同程式代碼、數據有效分離較好的體現 了面向對象的設計思想
2.可以避免由於java單繼承特性帶來的局限。。即當我們要將已經繼承了某一個類的子類 放入多線程中,
由於一個類不能同時有兩個父類所以不能用繼承Thread類的方式。所以就只能用Runnable介面來實現了
3.有利於程式的健壯性,代碼能夠被多個線程共用,代碼與數據是獨立的,多個線程可以操作相同的數據,與代碼無關。當共用相同的對象時,即共用相同的數據。當線程被構造時,需要的代碼和數據通過 一個對象 作為構造函數實參傳遞進去,這個對象 就是一個實現 了Runnable介面的類的實例。
後臺線程與聯合線程:
1.後臺線程與setDaemon方法
簡單的說吧前臺線程結束這個程式就結束。只要有一個前臺線程在運行那麼就沒有結束如果只有後臺線程在運行那麼這個程式就會掛掉
我們以前用到的都是前臺進程,如果對某個線程對象 啟動(start方法)之前調用了setDaemon(true)方法,這個線程就會變成後臺線程。
public class DemonThread { public static void mian(String[] args) { ThreadTest t=new ThreadTest(); Thread tt=new Thread(t); tt.setDaemon(true); tt.start(); } } // 何問起 hovertree.com class ThreadTest implements Runnable { public void run() { while(true) { System.out.println(Thread.currentThread().getName()+" is running.") } } }//這裡我們雖然創建了一個無限迴圈的線程,因為他是後臺線程整個進程在主線程結束時就終止了運行。這就說明瞭上面的理論。
2.聯合線程與join方法:
join(long millis)和join(long millis,int nanos)這是兩個帶參數的join方法,他們是兩個線程合併指定的時間後又開始分離回到以前的樣子
還有一個是無參數的join方法
方法名.join(),即pp.join();語句 他的作用就是把pp所對應的線程合併到調用 pp.join();語句的線程中
多線程在實際中的應用:
如何將資料庫一個表中的所有記錄複製到另一個表中,
1 boolean bFlag =true; 2 while(bFlag) 3 { 4 複製程式 5 }
多線程的同步:
線程安全問題:
問題原因
當多條語句在操作同一個線程共用數據時,一個線程對金條語句只執行 了一部分。還沒有執行【無,另一個線程參與進來,導致了共用數據的錯誤
解決方法
對多條操作共用數據的語句,只能讓一個線程都執行完,在執行的過程中,其他線程不可以參與執行
或者說吧:就是一個資源分配的問題。
死鎖的必要條件(也是解決線程安全問題的一個方法如果線程出現了死鎖我們就可以用下麵這些方式來幹掉他)
1.互斥地使用資源 第個資源只能給一個進程使用
2.占有且等待資源 一個進程 申請資源得不到滿足時處於等待資源的狀態,且不釋放占用資源
3.非搶奪式分配資源 任何一個進程 不能搶奪另一個線程所占的資源,也就是被占用資源只能由占用線程自己來釋放
4迴圈等待資源 存一組隊線程,其實第個線程分別等待另一個進程所占用的資源
對多條操作共用數據語句,只能讓一個線程都執行完,在執行過程中其他線程不可以參與執行
java對待這些問題又提供了專業的解決方法
synchronized(對象)
{
需要被同步的代碼
}對象如同鎖,持有鎖的線程可以在同步中執行。沒有持有鎖的線程
即使獲取cpu的執行權,也進不去,因為沒有獲取鎖。
線程睡眠是可以被打斷的通過Thread.interrupt()當然一個線程中呆以調用另外一個線程的interrupt(),線程睡眠被打斷後進入Runnable狀態。由於Thread.sleep()的定義中的
trows關鍵字聲明該方法中有可能引發異常所以我們的程式在調用該方法時必須使用try....catch代碼塊處理,否則編譯將出錯。這正是java語句強健性的一個方面。
同步代碼塊:
如果代碼不同步可能會出現很多的安全問題所以我們要想一個辦法來解決?那麼同步是最好的辦法
同步要做到些啥呢?用一個例子來說:就好比宿舍的單人床吧!如果 是兩個人睡到一起那麼肯定會掉下來一個,所以我們就只能一個一個的睡覺。
也就是程式中有多個線程同時在這兩句代碼之間但是只能有一個執行。
同步必須滿足下麵的三個好求:
1 必須要有兩個或者兩個以上的線程
2 必須是多個線程使用同一個鎖
3 必須保證同步代碼中只有一個線程在執行
好處:解決了多線程的安全問題。
弊端:耗費記憶體資源,每次進鎖都要判斷!也耗費時間
我們必須要明確他的共用數據。執行代碼、3 明確多線程運行代碼中哪些操語句是操作共用數據的。
synchrinized語句格式 synchrinized(ibject){代碼段}//object為任一對象。將具有原子性的代碼放入這個語句 內那麼就形成了同步代碼塊。在同一個時刻只能有一個線程可以進入同步代碼塊內運行。
// 何問起 hovertree.com class ThreadTest implements Runnable { private int tickets=100; String str=new String(""); public void run() { while(true) { synchronized(str) { if(ticket>0) { try { Thread.sleep(10); } catch(Exception e) { System.out.println(e.getMessage()); } System.out.println(Thread.currentThread().getName()"is saling ticket"+ticket--); } } } } }
同步函數
我們也可以對函數進行同步,只要在需要同步的函數定義前面加上syncthronized關鍵字即可。
class ThreadTest implements Runnable { private int tickets=100; public void run() { while(true) { sale(); } } // 何問起 hovertree.com public synchronized void sale()//這裡就是對函數進行同步。 { if(tickets>0) { try { Thread.sleep(10); } catch(Exception e) { System.out.println(e.getMessage()); } System.out,println(Thread.currentThread().getName()+"is saling ticket"+tickets--); } } }
在同一個類中,使用synchronized關鍵字定義的若幹方法 可以在多全線程之間同步。當一個線程進入了synachronized修飾的方法,
其他線程就不能進入同一個對象 所使用了synachronized修飾方法,直到第一個線程執行完它所進入 synchronized修飾的方法為止
代碼塊與函數間的同步:
線程同步靠的是檢查同一的標誌位,只要讓代碼塊與函數使用同一個監視器,那麼我們就可以做到讓代碼塊和函數同步了,類中的非靜態方法只能訪問本類對象,即this
也就是同步函數汽用的監視器對象只能是this。
1 public class ThreadDemo1 2 { 3 public static void main(String[]args) 4 { 5 ThreadTest t=new ThreadTest(); 6 new Thread(t).start();//這個線程調用同步代碼塊 7 try{Thread.sleep(1);} 8 9 10 catch(Exception e){} 11 12 // 何問起 hovertree.com 13 t.str new String("method"); 14 new Thread(t).start();//這個線程調用同步函數 15 } 16 } 17 class ThreadTest implements Runnable\ 18 { 19 private int tickets=100; 20 String str=new String(""); 21 public void run() 22 { 23 if(str.equals("method"))//這裡不能用==比較運行符因為是比較值,也就成了比較地址值。所以我們這裡要用equals因為我們比較的是內容 24 { 25 while(true) 26 { 27 sale(); 28 } 29 } 30 else 31 { 32 synchronized(this)//這樣才會實現同步如果不是this 是str可能不會同步 33 { 34 if(tickets>0) 35 { 36 try 37 { 38 Thread.sleep(10); 39 } 40 catch(Exception e) 41 { 42 System.out.println(e.getMessage()); 43 }System.out.print("Test: ");//一個檢測語句 44 System.out,println(Thread.currentThread().getName()+"is saling ticket"+tickets--); 45 } 46 } 47 } 48 } 49 public synchronized void sale() 50 { 51 if(tickets>0) 52 { 53 try 54 { 55 Thread.sleep()10; 56 } 57 catch(Exception e) 58 { 59 System.out.println(e.getMessage); 60 } 61 System.out,println(Thread.currentThread().getName()+"is saling ticket"+tickets--); 62 } 63 } 64 }
public void push(shar c) { data[idx]=c; idx++; }
如果一個線程剛執行完push方法中的data方法中的data[idx]=c語句,CPU便切換到另外一個線程上執行push方法,第二個線程將覆蓋第一個線程執行的data[idx]=c語句的結果
還有共用訪問數據,應當是類的private數據成員,從而禁止來自類外的隨意訪問破壞數據的一致性。
上面的代碼如果這樣寫容易出現安全問題
死鎖:
什麼是死鎖呢?
簡單的說嘛:就是個犯罪分子在搶了銀行後拿起槍 ,抓起人質威脅警察,警察想救人,犯罪分子想走,然後犯罪的叫警察先給他準備東西然後再放人質。而且警察要他先放人質在給他東西讓他走。最後就誰也不讓。然後就出現了兩邊對峙。誰都不幹的狀況也就導致事件不能繼續下去。
解決方法:
就是讓一個線程先執行完然後再執行另外的就OK了也就是同步可以解決死鎖問題
線程間通信:
其實就是多個線程在操作同一個資源。但是操作的動作不同。
等待喚醒機制:兩個或多個線程同時訪問共用數據
wait() 釋放資源,釋放鎖 sleep() 釋放資源,不釋放鎖。 notify() 喚醒最先wait的線程。 notifyAll()喚醒所有wait的線程。
都是用在同步中,因為要對持有監視器(鎖)的線程操作
所以要使用在同步中,因為只有同步才具有鎖。
為什麼這些操作線程的方法要定義在object類中呢?
因為這些方法在操作同步線程時,都必須要標示它們所操作線程持有的鎖
只有同一個鎖上的被等待線程,可以被同一個鎖上notify喚醒
不可以對不同鎖中的線程喚醒。
也就是說,等待和喚醒必須是同一個鎖。
而鎖可以是任意對象,鎖可以被任意對象調用的方法定義在object類
也可以用前面 說到的 join 當a線程執行到了b線程的join方法時,那麼a線程就會等待,等b線程都執行完
a才會執行。join可以用來臨時加入線程執行。
優先順序
所有線程預設優先順序都是5 1<=優先順序<=10 MIN_PRIORTY MORM_PRIORTYMAX_PRIORTY
setPriority(int);設置優先順序
stop方法已經過時
所以停止線程只有一種,run方法結束。
開啟多線程運行,運行代碼通常是迴圈結構。
只要控制住迴圈,就可以讓run方法結束,即線程結束
特殊情況
當線程處於凍結狀態。就不會讀取到標記,線程就不會結束
當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時需要對凍結進行清除。
強制讓線程恢復到運行狀態中來,這樣就可以操作標記讓線程結束
Thread類提供方法 interrupt();
線程生命的控制:
任何東西都有一個生命周期,線程也是如此
線程運行過程中的去向:
1.沒有遇到任何阻隔,運行完成直到結束,也就是run()方法執行完畢
2.調度器CPU分配給其他線程,這個線程Runnable狀態
3.請求鎖旗標卻得不到,這個時候 它要等待對象的鎖旗標,得到鎖旗後又會進入Runnable狀態開始運行
4.遇到wait方法它會被放入等待池中繼續待會,直到有notify()或interrupt()方法執行,它才會被 喚醒或打斷開始等待對象 鎖旗標,等 到進入Runnable狀態繼續執行
其實控制線程生命周期的方法有很多種。 suspend resume stop方法便是不推薦使用這三種
suspend 和stop方法 會導致死鎖 它允許A線程直接控制B線程的代碼來直接控制B線程
STOP會導致數據不完整性
推薦使用控制 run方法中迴圈條件的方式來結束一個線程。
推薦:http://www.cnblogs.com/roucheng/p/kaifagainian.html