進程:正在執行的程序,是一個動態的過程
線程:是進程中用于控制程序執行的控制單元(執行路徑,執行情景)
進程中至少有一個線程。
在Java VM(java虛擬機)啟動的時候會有一個進程java.exe.
該進程中至少一個線程負責java程序的執行。
而且這個線程運行的代碼存在于main方法中。
該線程稱之為主線程。
擴展:其實更細節說明jvm,jvm啟動不止一個線程,還有負責垃圾回收機制的線程。
創建線程
創建線程的第一種方式:繼承Thread類
步驟:
1,定義類繼承Thread。
2,復寫Thread類中的run方法。
目的:將自定義代碼存儲在run方法。讓線程運行。
3,調用線程的start方法,
該方法兩個作用:啟動線程,調用run方法。
發現運行結果每一次都不同。因為多個線程都獲取cpu的執行權。cpu執行到誰,誰就運行。
明確一點,在某一個時刻,只能有一個程序在運行(多核除外)。cpu在做著快速的切換,以達到看上去是同時運行的效果。
我們可以形象把多線程的運行形容為在互相搶奪cpu的執行權。
這就是多線程的一個特性:隨機性。誰搶到誰執行,至于執行多長,cpu說的算。
為什么要覆蓋run方法呢?
Thread類用于描述線程。該類就定義了一個功能,用于存儲其他線程(非主線程)要運行的代碼。該存儲功能就是run方法。
也就是說Thread類中的run方法,用于存儲線程要運行的代碼。
創建線程的第二種方式:實現Runable接口
步驟:
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方法。
線程中的方法
線程都有自己默認的名稱:Thread-編號 該編號從0開始。
線程中的常用方法
static Thread currentThread():獲取當前線程對象。
getName(): 獲取線程名稱。
設置線程名稱:setName或者構造函數。
多線程中出現的問題
多線程的運行出現了安全問題。
問題的原因:
當多條語句在操作同一個線程共享數據時,一個線程對多條語句只執行了一部分,還沒有執行完,
另一個線程參與進來執行。導致共享數據的錯誤。
解決辦法:
對多條操作共享數據的語句,只能讓一個線程都執行完。在執行過程中,其他線程不可以參與執行。
Java對于多線程的安全問題提供了專業的解決方式。
就是同步代碼塊,方法如下
synchronized(對象)//任意對象都行
{
需要被同步的代碼
}
對象如同鎖。持有鎖的線程可以在同步中執行。
沒有持有鎖的線程即使獲取cpu的執行權,也進不去,因為沒有獲取鎖。
同步的前提:
1,必須要有兩個或者兩個以上的線程。
2,必須是多個線程使用同一個鎖。
必須保證同步中只能有一個線程在運行。
好處:解決了多線程的安全問題。
弊端:多個線程都需要判斷鎖,較為消耗資源,
示例代碼如下:
public class TicketDemo2 {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket implements Runnable {
private int tick = 100;
Object obj = new Object();// 為synchronized提供對象
public void run() {
while (true) {
synchronized (obj) {
if (tick > 0) {
try{Thread.sleep(50);}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "....sale : " + tick--);
}
}
}
}
}
此段代碼的是模擬賣票,假設有100張票,然后有4個線程同時賣票。
可以在Ticket類中有synchronized同步代碼塊,當有同步代碼塊時,持有synchronized同步鎖的線程可以在同步中執行。沒有持有鎖的線程即使獲取cpu的執行權,也進不去,因為沒有獲取synchronized同步鎖。線程將依序執行,也就是有一個線程在進入時,另一個線程無法進入。
如果沒有synchronized同步鎖,那么有可能會有以下問題產生,當tick剩下1張票的時候,t1現場進入了,然后遇到Thread.sleep(50),失去執行權。
接著t2線程執行了了,此時tick仍然是1張票,因此t2也進入if語句,也遇到sleep()方法,失去了執行權。
然后t3線程也來了,遇到了與t1/t2一樣的狀況。
t4線程也既有可能遇到這種情況。
最后t1開始執行了,tick為0了。但是此時t2也在if語句中,也執行語句,tick就為-1,然后是t3/t4,導致了代碼運行的結果出現了負數,與設想中的不同!
上面所描述的問題就是多線程中所要解決的問題,這也是synchronized同步鎖的使用情景
同步鎖
synchronized可以給代碼塊上鎖,也可以給方法上鎖,如下:
// 同步代碼塊
synchronized (對象) {
····· 代碼塊
}
// 同步方法
public synchronized void show (){}
那么問題來了,在同步代碼塊中我們可以很清楚的看到synchronized給哪一個對象上鎖,那么同步方法又是給誰上鎖呢?
同步函數用的是哪一個鎖呢?
函數需要被對象調用。那么函數都有一個所屬對象引用,就是this,所以同步函數使用的鎖是this。
通過該程序進行驗證。
使用兩個線程來買票。一個線程在同步代碼塊中。一個線程在同步函數中。都在執行買票動作。
代碼如下:
public class TicketDemo2 {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
// 讓主線程休眠,將執行權移交給t1/t2線程
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
class Ticket implements Runnable {
private int tick = 100;
Object obj = new Object();
// 使用flag
boolean flag = true;
public void run() {
if (flag) {
while (true) {
// 使用的鎖是obj
synchronized (obj) {
if (tick > 0) {
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "....code : " + tick--);
}
}
}
} else
while (true)
show();
}
// 同步方法的鎖對象是this,即所屬對象的引用
public synchronized void show() {
if (tick > 0) {
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "....show.... : " + tick--);
}
}
}
上述代碼中添加了同步方法,并且設置了flag布爾值,用以讓線程能在中途從同步代碼塊中切換到同步方法中。
然后再控制臺中看到結果:
Thread-1....show.... : 4
Thread-0....code : 3
Thread-1....show.... : 2
Thread-1....show.... : 1
Thread-0....code : 0
這里居然賣出了0張票,這明顯是一個錯誤的結果,這是為什么呢?
答案在于我們雖然在多線程中使用了同步鎖,但是我們的鎖對象并不是同一個對象,因為在同步代碼塊中使用的是obj對象,如下:
synchronized (obj) {
·····代碼省略
}
但是當obj替換成this的時候,我們就能輸出正確的結果,不在輸出0張票。
這足以證明在同步方法中使用的鎖對象就是所屬對象引用。
靜態同步方法
static實際上也能使用同步鎖,我們將上述代碼修改,首先將同步方法修改為靜態同步方法,然后將tick也標識為static,代碼如下:
private static int tick = 100;
···
public static synchronized void show(){
····
}
調用方法跟前面一樣。
這時再次輸出了tick=0的錯誤結果!
這又是為什么呢?
這是因為靜態方法中的同步鎖對象使用的不是this,因為靜態方法中也不可以定義this。
其原因在于靜態進內存時,內存中沒有本類對象,但是一定有該類對應的字節碼文件對象。
即 類名.class 該對象的類型是Class
靜態的同步方法,使用的鎖是該方法所在類的字節碼文件對象。 類名.class。
所以我們將同步代碼塊中的所對象修改為Ticket.class時就能輸出正確結果,此時Ticket完整代碼如下:
class Ticket implements Runnable {
private static int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (Ticket.class) {
if (tick > 0) {
try{Thread.sleep(50);}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "....code : " + tick--);
}
}
}
} else
while (true)
show();
}
// 靜態方法的所對象是 類名.class
public static synchronized void show() {
if (tick > 0) {
try{Thread.sleep(50);}catch(Exception e){}
System.out.println(Thread.currentThread().getName() + "....show.... : " + tick--);
}
}
}
單例模式中的懶漢式
單例模式中的懶漢式寫法其實頗為復雜,因為要考慮到多線程的問題。如果不添加synchronized進行同步,在多線程的情況下,仍有可能導致創建了多個單例的實例,這就違背了單例模式的設計出初衷,因此必須添加synchronized進行同步,正確寫法如下:
class Single {
private static Single s = null;
private Single() {
}
public static Single getInstance() {
if (s == null) {
synchronized (Single.class) {
if (s == null)
s = new Single();
}
}
return s;
}
}
懶漢式與餓漢式的區別在于懶漢式用于延時加載。
而懶漢式出現的問題在于使用多線程的時候創建多個實例,這時可以使用同步解決。
使用synchronized同步也是有技巧的,如果使用同步方法也是可以的,代碼如下:
public synchronized static Single getInstance() {
if (s == null) {
if (s == null)
s = new Single();
}
return s;
}
但這種寫法會導致效率稍微低下,因此一般都采用雙重判斷的同步代碼塊的寫法。
同時如果是使用了static靜態符合,靜態代碼塊所使用的同步鎖為該類所屬的字節碼對象!
死鎖
產生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進程使用。
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
同步中嵌套同步,但是它們之間的鎖卻不同,容易導致死鎖,下面是死鎖示例:
public DeadLockTest TicketDemo2 {
public static void main(String[] args) {
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
class Test implements Runnable {
private boolean flag;
Test(boolean flag) {
this.flag = flag;
}
public void run() {
if (flag) {
while (true) {
synchronized (MyLock.locka) {
System.out.println(Thread.currentThread().getName() + "...if locka ");
synchronized (MyLock.lockb) {
System.out.println(Thread.currentThread().getName() + "..if lockb");
}
}
}
} else {
while (true) {
synchronized (MyLock.lockb) {
System.out.println(Thread.currentThread().getName() + "..else lockb");
synchronized (MyLock.locka) {
System.out.println(Thread.currentThread().getName() + ".....else locka");
}
}
}
}
}
}
class MyLock {
static Object locka = new Object();
static Object lockb = new Object();
}
在上面的例子中Test在t1線程中進入if語句得到了MyLock.locka的鎖,然后進入了第二個代碼塊中需要MyLock.lockb的鎖才能進行下一步。
但是此時t2線程也開始執行了,它先進入了Test中的else語句,獲得了MyLock.locka的鎖,需要MyLock.locka的所完成同步代碼。
此時陷入了死局,t1持有了MyLock.locka的鎖,但是無法獲得MyLock.lockb的鎖執行完線程,也無法釋放MyLock.locka的鎖。
而t2線程同樣如此,持有MyLock.lockb的鎖,但是無法獲得MyLock.locka的鎖執行完線程,也無法釋放MyLock.locka的鎖。
在寫程序的時候應該避免死鎖的產生。
線程間通信
線程間通訊: 其實就是多個線程在操作同一個資源,但是操作的動作不同。
wait:
notify();
notifyAll();
notify()方法用于喚醒等待中的線程,一般線程使用了wait()方法,會進入線程池中等待執行,此時使用notify()方法一般喚醒線程池中的第一個等待線程。
都使用在同步中,因為要對持有監視器(鎖)的線程操作。
所以要使用在同步中,因為只有同步才具有鎖。
為什么這些操作線程的方法要定義Object類中呢?
因為這些方法在操作同步中線程時,都必須要標識它們所操作線程所持有的鎖,
只有同一個鎖上的被等待線程,可以被同一個鎖上notify喚醒。
不可以對不同鎖中的線程進行喚醒。
也就是說,等待和喚醒必須是同一個鎖。
而鎖可以是任意對象,所以可以被任意對象調用的方法定義Object類中。
多線程生產消費者示例
下列代碼為正確示例:
class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*
* 對于多個生產者和消費者。 為什么要定義while判斷標記。 原因:讓被喚醒的線程再一次判斷標記。
* 為什么定義notifyAll, 因為需要喚醒對方線程。 因為只用notify,容易出現只喚醒本方線程的情況。導致程序中的所有線程都等待。
*/
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
public synchronized void set(String name) {
while (flag)
try {this.wait();} catch (Exception e) {} // t1(放棄資格) t2(獲取資格)
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生產者.." + this.name);
flag = true;
this.notifyAll();
}
// t3 t4
public synchronized void out() {
while (!flag)
try { wait();} catch (Exception e) {} // t3(放棄資格) t4(放棄資格)
System.out.println(Thread.currentThread().getName() + "...消費者........." + this.name);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
res.set("+商品+");
}
}
}
class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
res.out();
}
}
}
在多線程的情況下,通用的做法是使用while循環 + notifyAll()喚醒方法來判斷執行代碼。
而在單生產者和單消費者的情況下使用if循環 + notify()來判斷執行代碼。
首先分析一下使用if循環 + notify()在多生產者和多消費者中產生的問題,使用這種方法會導致程序執行兩次生產,一次消費的情況,又或者是兩次消費一次生產的情況。
關鍵代碼如下:
public synchronized void set(String name)
{
if(flag)
try{this.wait();}catch(Exception e){}//t1(放棄資格) t2(獲取資格)
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生產者.."+this.name);
flag = true;
this.notify();
}
// t3 t4
public synchronized void out()
{
if(!flag)
try{wait();}catch(Exception e){}//t3(放棄資格) t4(放棄資格)
System.out.println(Thread.currentThread().getName()+"...消費者........."+this.name);
flag = false;
this.notify();
}
產生這種情況的原因在于如果生產者(t1/t2)先獲得了執行權,此時flag為false,因此t1直接往下執行,生產出一個產品(執行println),然后將flag設為true,但是此時t1仍然能繼續執行(本案例中線程沒有退出機制,因此一旦執行將持續執行不停止),于是t1再次在if語句中判斷,然而flag已經為true,所以t1執行了wait()方法,放棄執行權。
重點來了,在這個時候t2生產者,t3/t4消費者都有執行權,假設這時t2取得執行權,執行下去了,此時flag仍為true,因此t2也進如wait()階段。
然后就輪到t3/t4執行了,他們消費一次之后,將flag設為false,然后喚醒了在線程池中的線程,而notify()會喚醒處于線程池中的第一個等待的線程,也就是t1。然而此時t3仍舊執行,但是遇到flag為false,于是t3執行了wait()方法。
這時線程中擁有執行權的就剩下生產者t1和消費者t4了,然而計算機執行了t4線程,因為flag為false的緣故,t4也等待了。
此時在線程池中等待的順序依次為 t2,t3,t4。
t1開始執行了,然后把flag設置為true,并且執行了notify()方法,喚醒了t2線程。
但是之前t2已經通過了if的判斷,處于wait()狀態,因此被喚醒的時候不在執行if判斷,直接往下執行,因此在t1之后也執行了生產命令。
最后喚醒了t3消費者,然后繼續執行。
這就是聯系兩次執行了生產命令的原因。
上面說的有點啰嗦,但這非常重要,需要細細體會。
了解了為什么在多生產/消費者執行的情況下,使用if判斷和notify()方法喚醒會持續執行兩次生產或者消費的命令后,這是因為if只判斷一次flag的情況,當線程被喚醒之后會直接往下執行的緣故。
那么我們如果使用while加上notify()方法,讓線程每次被喚醒的時候都進行判斷呢?
這時產生了死鎖。
這個結束比較簡單,首先t1運行,flag為true,然后t1繼續執行的時候就進入wait()方法,如果此時t2執行的話,也進入wait()方法中。
然后t3/t4執行了,t3將flag設為false,并喚醒了t1,然后t3繼續執行便執行了wait()方法,如果此時t4執行了,也會進入wait()方法中,這時消費者線程全滅
最后剩下t1在執行了,t1將flag設置true之后,喚醒了t2,但是此時flag為true,而每次喚醒都會在while進行一次循環,此時悲催的t2再次進入wait()方法,生產者線程也全滅了!
因為只用notify,容易出現只喚醒本方線程的情況。導致程序中的所有線程都等待。
這就是產生死鎖的原因。
所以在多生產多消費者的情況下使用while讓每個線程在喚醒的時候再次進行判斷,然后使用notifyAll()喚醒所有的線程,讓每個線程再次做判斷是否繼續往下執行,這樣才能確保每次都有線程能夠執行,也能確保每個線程都能被喚醒,不會導致死鎖。
使用jdk1.5之后的Lock接口進行同步
上面生產者和消費者的使用方法太過繁瑣了,JDK1.5 中提供了多線程升級解決方案。
將同步Synchronized替換成現實Lock操作。
將Object中的wait,notify notifyAll,替換了Condition對象。該對象可以Lock鎖 進行獲取。
在下列示例中,實現了本方只喚醒對方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
我們修改上面生產者和消費者案例中的Resource的代碼,將Synchronized替換為Lock,將notifyAll替換為Condition,代碼如下:
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name) throws InterruptedException {
lock.lock();
try {
while (flag)
condition_pro.await();// t1,t2等待
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生產者.." + this.name);
flag = true;
condition_con.signal(); // 喚醒t3,t4中的一個
} finally {
lock.unlock();// 釋放鎖的動作一定要執行。
}
}
// t3 t4
public void out() throws InterruptedException {
lock.lock();
try {
while (!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName() + "...消費者........." + this.name);
flag = false;
condition_pro.signal();
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
try {
res.set("+商品+");
} catch (InterruptedException e) {
}
}
}
}
class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while (true) {
try {
res.out();
} catch (InterruptedException e) {
}
}
}
}
使用Condition的好處在于一個Lock鎖可以擁有多個Condition對象。
而在上面的代碼中定義了兩個Condition對象,一個為生產者condition_pro的條件,另一個為消費者condition_con條件。
這使condition_pro可以使用自己的await()方法和condition_pro.signal()喚醒方法,這樣就能讓在生產者中喚醒消費者,而在消費者中喚醒生產者,不會發生像Synchornized中的那種喚醒了本方線程的失誤事件。
但是要注意,使用Lock的時候,一定要在finally中釋放鎖,即調用lock.unlock()方法。
停止線程的方式
stop方法已經過時。
如何停止線程?
只有一種,run方法結束。
開啟多線程運行,運行代碼通常是循環結構。
只要控制住循環,就可以讓run方法結束,也就是線程結束。
示例代碼中給Runnable對象添加了flag標記,控制了while循環,代碼如下:
class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while (true) {
if (num++ == 60) {
st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "......." + num);
}
System.out.println("over");
}
}
class StopThread implements Runnable {
private boolean flag = true;
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + "....run");
}
}
public void changeFlag() {
flag = false;
}
}
特殊情況:
當線程處于了凍結狀態。
就不會讀取到標記。那么線程就不會結束。
當沒有指定的方式讓凍結的線程恢復到運行狀態是,這時需要對凍結進行清除。
強制讓線程恢復到運行狀態中來,這樣就可以操作標記讓線程結束。Thread類提供該方法 interrupt();
示例如下:
class StopThread implements Runnable {
private boolean flag = true;
public synchronized void run() {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "....Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "....run");
}
}
public void changeFlag() {
flag = false;
}
}
class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while (true) {
if (num++ == 60) {
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "......." + num);
}
System.out.println("over");
}
}
要注意的是使用 interrupt();方法并不是正確的停止線程的方式,而是以拋出異常的方式停止線程,要慎用。
Thread中的其他方法
setDaemon(boolean on) 將該線程標記為守護線程或用戶線程。簡單的說就是將線程設置為后臺線程,此時將會與主線程搶奪CPU資源,而且主線程結束時,后臺線程會自動結束。
修改上面的StopThreadDemo代碼,將t1/t2線程設置為后臺線程,如下
class StopThreadDemo {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while (true) {
if (num++ == 60) {
break;
}
System.out.println(Thread.currentThread().getName() + "......." + num);
}
System.out.println("over");
}
}
class StopThread implements Runnable {
private boolean flag = true;
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + "....run");
}
}
public void changeFlag() {
flag = false;
}
}
此時并沒有使用任何的方式結束線程,卻發現當主線程結束時,t1/t2線程也結束了。
join()方法與yield()方法
join:
當A線程執行到了B線程的.join()方法時,A就會等待。等B線程都執行完,A才會執行。
join可以用來臨時加入線程執行,代碼示例:
class JoinDemo {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
for (int x = 0; x < 80; x++) {
System.out.println("main....."+x);
}
System.out.println("over");
}
}
class Demo implements Runnable {
public void run() {
for (int x = 0; x < 70; x++) {
System.out.println(Thread.currentThread().toString() + "....." + x);
}
}
}
toString():返回該線程的字符串你表示形式,包括線程名稱、優先級和線程組
yield():暫停當前正在執行的線程對象,并執行其他線程,代碼示例:
class YieldDemo {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
System.out.println("over");
}
}
class Demo implements Runnable {
public void run() {
for (int x = 0; x < 70; x++) {
System.out.println(Thread.currentThread().toString() + "....." + x);
Thread.yield();
}
}
}
線程間通訊
Java線程間通訊其實也就是使用Synchronized和Object類方法wait(),notify(),notifyAll()的共同使用,保證運行結果正確,這些都在上述代碼有所涉及。