java学习笔记(九)----多线程
class ThreadDemo1
{ public static void main(String[] args)
? { new TestThread().start(); //用start()默認去調用run()方法
??? while(true)
??? { System.out.println("main():"+Thread.currentThread().getName()); }
? }
}
class TestThread extends Thread
{ public void run()
? { while(true)
??? { System.out.println("run():"+Thread.currentThread().getName()); }
? }
}
1.要將一段代碼在一個新的線程上運行,該代碼應該在一個類的run函數中,并且run函數所在的類是Thread的子類。 倒過來看,我們要實現多線程,必須編寫一個繼承了Thread類的子類,子類要覆蓋Thread類中的run函數,在子類的run函數中調用想在新線程上運行的程序代碼。
2.啟動一個新的線程,我們不是直接調用Thread子類對象的run方法,而是調用Thread子類對象的start(從Thread類的繼承到的)方法,Thread類對象的start方法將產生一個新的線程,并在該線程上運行該Thread類對象中的run方法。
3.由于線程的代碼段在run方法中,那么該方法執行完成以后線程也就相應的結束了,因而我們可以通過控制run方法中循環的條件來控制線程的結束.
----------------------------------------------------------------------------------
***后臺線程***
class ThreadDemo1
{ public static vioid main(String[] args)
? { Thread tt=new TestThread();
??? tt.setDaemon(true);? //在start()前加了setDaemon(true)方法,run()中的線程就變成了后臺線程
??? tt.start();
??? while(true)
??? { System.out.println("main():"+Thread.currentThread().getName()); }
? }
}
class TestThread extends Thread
{ public void run()
? { while(true)
??? { System.out.println("run():"+Thread.currentThread().getName()); }
? }
}
說明了,java中只有后臺線程,沒有前臺線程,java程序就會結束。
1.如果我們對某個線程對象在啟動(調用start方法)之前調用了setDaemon(ture)方法,這個線程就變成了后臺線程。
2.對java程序來說,只要有一個前臺線程在運行,這個進程就不會結束,如果一個進程中只有后臺線程運行,這個進程就會結束。
-----------------------------------------------------------------------------
***主線程和子線程的合并***
class ThreadDemo1
{ public static vioid main(String[] args)
? { Thread tt=new TestThread();
??? tt.setDaemon(true);?
??? tt.start();
??? int index=0;
??? while(true)
??? { if(index++==100)
?????????? try{tt.join();} catch(Exception e){}? //此處會有異常,所以必須用try,catch
?? //當主線程運行100遍以后,就把tt對象的線程加入主線程,又成為單線程了,只有tt對象的線程執行完以后,才會繼續執行main中的主線程。
//? try{tt.join(10000);} catch(Exception e){} 當主線程和子線程合并100000毫秒后,在分開運行
????? System.out.println("main():"+Thread.currentThread().getName());
??? }
? }
}
class TestThread extends Thread
{ public void run()
? { while(true)
??? { System.out.println("run():"+Thread.currentThread().getName()); }
? }
}
---------------------------------------------------------------------------------------------------------
***創建線程另一種方法,用Runnable接口***
class ThreadDemo1
{ public static vioid main(String[] args)
? { Thread tt=new Thread(new TestThread()); //創建一個thread的對象, 當線程對象啟動時,它就不調用thread類中自帶的run()方法,而記為調用Runnalbe中的run()方法
??? tt.start();
??? int index=0;
??? while(true)
??? { if(index++==100)
?????????? try{tt.join();} catch(Exception e){}?
????? System.out.println("main():"+Thread.currentThread().getName());
??? }
? }
}
class TestThread implements Runnable //它繼承Runnable接口,Runnable接口就有一個run()方法
{ public void run()
? { while(true)
??? { System.out.println("run():"+Thread.currentThread().getName()); }
? }
}
----------------------------------------------------------------------------------------
***用多個線程,去執行一段代碼***
帖路售票,四個窗口同時賣100張票,需要四個線程一同去執行一段代碼。
class ThreadDemo1
{ public static void main(String[] args)
? { new TestThread().start();
new TestThread().start();
new TestThread().start();
new TestThread().start();
? /*這樣就是4個線程,但每個線程都是執行各自的代碼,它們是在賣各自的100張票,并不是同時執行一段代碼。
應改為
TestThread tt=new TestThread();
??? tt.start();
??? tt.start();
??? tt.start();
??? tt.start();這樣還是不行,因為這樣就算寫再多tt.start()它還是一個線程,
將main中代碼改為
TestThread tt=new TestThread();
??? new Thread(tt).start();
??? new Thread(tt).start();
??? new Thread(tt).start();
??? new Thread(tt).start();
只有讓TestThread類繼承Runnable類,這樣寫才是四個線程一起去執行同一段代碼*/
? }
}
class TestThread extends Thread 改為 class TestThread implements Runnable
{ int tickets=100;
? public void run()
? { while(true)
??? { if(tickets>0)?
?????? { try{Thread.sleep(10);} catch(Exception e){}? //Thread.sleep(10)讓該線程停10毫秒,其它線程繼續執行
???????? System.out.println(Thread.currentThread().getName()+"is saling ticket"+tickets--);
?????? }
??? }
? }
}
***使用Runnable接口創建多線程***
1.適合多個相同程序代碼的線程去處理同一資源的情況,把虛擬CPU(線程)同程序的代碼、數據有效分離,較好地體現了面向對象的設思想
2.可以避免由于java的單繼承特性帶來的局限。我們經常碰到這樣一種情況,即當我們要將已經繼承了某一個類的子類放入多線程中,由于一個類不能同時有兩個父類,所以不能用繼承Thread類的方式,那么,這個類就只能采用實現Runnable
接口。
3.當線程被構造時,需要的代碼和數據通過一個對象作為構造函數實參傳遞進去,這個對象就是一個實現了Runnable接口的類的實例?? (這句看不太懂)
4.事實上,幾乎所有多線程應用都可用Runnable接口方式。
----------------------------------------------------------------------------
----------------------------------------------------------------------------
***************多線程的同步*******************
class ThreadDemo1
{
TestThread tt=new TestThread();
??? new Thread(tt).start();
??? new Thread(tt).start();
??? new Thread(tt).start();
??? new Thread(tt).start();
? }
}
class TestThread implements Runnable
{ int tickets=100;
? public void run()
? { while(true)
??? { if(tickets>0)?
?????? { try{Thread.sleep(10);} catch(Exception e){}? //Thread.sleep(10)讓該線程停10毫秒,其它線程繼續執行
???????? System.out.println(Thread.currentThread().getName()+"is saling ticket"+tickets--);
?????? }
??? }
? }
}
//上面的程序在執行時,就會打印出負數來
?把TestThread 類改為
class TestThread implements Runnable
{ int tickets=100;
? ? String str=new String(""); //為synchronized定義的對象, 這個對象的定義必須放在run()方法的外面,不能放在里面,如果放里面的話,每個線程調用時,都產生一個新的str對象,這樣,每個線程的str對象每回的標志都是1開始,它就失去了synchronized的功能,運行后還會出現負數。
??? public void run()
? { while(true)
??? { synchronized(str)?? //將代碼放入synchronized語句內,形成了同步代碼塊,在同一時刻只能有一個線程可以進入同步代碼塊內運行,當該線程離開同步代碼塊后,其他線程才能進入同步代碼塊。 synchronized(object){代碼塊}這里的object可以是任意一個對象。
{ if(tickets>0)?
????? ? { try{Thread.sleep(10);} catch(Exception e){}
??????? ? System.out.println(Thread.currentThread().getName()+"is saling ticket"+tickets--);
?????? }
??????? }
??? }
? }
}
//還可以把上面的TestThread改為
class TestThread implements Runnable
{ int tickets=100;
? ?
??? public void run()
? { while(true)
????? { sale(); }
? }
?? public synchronized void sale() //這樣調用也可以,在sale()方法前加上synchronized,這樣運行后就不會有負數, 這個方法是用this作為同步監視器的。
??? {? if(tickets>0)
?????? { try(Thread.sleep(10);) catch(Exception e){}
????????? System.out.println(Thread.currentThread().getName()+"is saling ticket"+tickets--);
?????? }
??? }
}
-------------------------------------------------------------------
***代碼塊與函數間的同步***
class ThreadDemo1
{
TestThread tt=new TestThread();
??? new Thread(tt).start();
try{Thread.sleep(1);}? catch(Exception e) {} //讓cpu延遲一毫秒,第一個線程就會去運行run()方法中的內容
??? tt.str=new String("method");
??? new Thread(tt).start();
? }
}
class TestThread implements Runnable
{ int tickets=100;
? ? String str=new String("");
??? public void run()
? { if(str.equals("method")
{
???????? while(true)
???????? { sale();
???????? }
?????? }
????? else?
?????? { while(true)
??? ??? { synchronized(this)? //此處注意,不能用str,這樣就和sale方法用的是同一個對象了,這樣就能實現同步了??
?? { if(tickets>0)?
????? ??? {?
??????? ? System.out.println(Thread.currentThread().getName()+"is saling ticket"+tickets--);
???????? }
?????????? }
??? ??? }
? ?? }
?
????? public synchronized void sale()
??? {? if(tickets>0)
?????? { try(Thread.sleep(10);) catch(Exception e){}
System.out.print("sale():")
????????? System.out.println(Thread.currentThread().getName()+"is saling ticket"+tickets--);
?????? }
??? }
}
注意:1. 如果沒能 try{Thread.sleep(1);}? catch(Exception e) {}語句,最后就是所有線程都去調用sale方法,但加了這條語句,不管停多久,cpu就會馬上去運行run()方法中的內容。
2. synchronized(this) 這里必須用this,代碼塊與函數間才能用相同的對象作為同步監視器。
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
***線程間的通信***
class Producer implements Runnable
{ Q q;
?public Producer(Q q)
?{? this.q=q; }
???? public void run()
????? { int i=0;
??????? while(true)
???????? {? /*? synchronized(q)
?????????????? { if(q.bFull)
?????????????????? try{q.wait();} catch(Exeption e){}
????????????????? if(i==0)
????????????????? { q.name="zhangsan"
??????? ??? try{Thread.sleep(1);} catch(Exception e){}
??????? ??? q.sex="male";
???????? ???? }
????????????????? else
????????????????? { q.name="lisi";
???????????????????? q.sex="female";
????????????????? }
????????????????? q.bFull=true;
????????????????? q.notify();
??????????????? } */
????????????????????? // 在Q類中加了put和get方法后,上面注釋的代碼可改為
---------------------------------------------------
????????????????????????? if (i==0)
??????????????????????????? q.put("zhangsan","male")
????????????????????????? else
??????????????????????????? q.put("lisi","female");
??????????????????????? ---------------------------------------------------
????? i=(i+1)%2;
??? }
? }
}
class Consumer implements Runnable
{ Q q;
?public Consumer(Q q)
?{? this.q=q; }
? public void run()
? { while(true)
??? {???????? /*?? synchronized(q)
?????????????????? { if(!q.bFull)
???????????????????? try{ q.wait();} catch(Exception e){}?
???????????????????? System.out.print(q.name);
???????????????????? System.out.println(":"+q.sex);
???????????????????? q.bFull=false;
???????????????????? q.notify();
?????????????????? } */
?????????????? // 在Q類中加了put和get方法后,上面注釋的代碼可改為
????????????????? --------------------------------------------------
????????????????? q.get();
????????????????? --------------------------------------------------??
??? }
? }
}
class Q
{ String name="unknown";?? // private String name="unknown";?
? String sex="unknown";??? // private String sex="unknown";?
boolean bFull=false;???? // private boolean bFull=false;
-------------------------------------------------------------
? ** public synchronized void put(String name,String sex)
? { if (bFll)
????????? try{ wait(); }? catch(Exception e){}
??? this.name=name;
??? try{Thread.sleep(1);}? catch(Exception e) {}
??? this.sex=sex;
???? bFull=true;
??? notify();
? }
? ** public synchronized void get()
? {? if (!bFull)?
??????????? try{ wait(); }? catch(Exception e){}
???? System.out.print(name);
???? System.out.println(":"+sex);
?????? bFull=false;
????? notify();???????
? }
? -------------------------------------------------------------
}
class ThreadCommunation
{ public static void main(String[] args)
? { Q q=new Q();
??? new Thread(new Producer(q)).start();
??? new Thread(new Consumer(q)).start();
? }
}
--Object對象包括三個方法
wait: 告訴當前線程放棄臨視器并進入睡眠狀態直到其它線程進入同一監視器并調用notify為止。
notify:喚醒同一對象監視器中調用wait的第一個線程。用于類似飯館有一個空位后通知所有等候就餐的顧客中的第一個可以入座的情況
notifyAll: 喚醒同一對象監視器中調用wait的所有線程,具有最高優先級的線程首先被喚醒并執行。用于類擬某個不定期的培訓班終于招生滿額后,通知所有學員都來上課的情況。
-----------?
| thread t|
-----------
???? |
-----------------?
|synchronized(o)| 線程t得到對象o的lock旗標
-----------------
???? |
-----------
|o.wait() | 此時線程t被放置在對象o的等待線程池中,t自動釋放o的鎖旗標
-----------
???? |
------------
|o.notify()| 當另外的線程執行了對象o的notify()方法后,線程t可能會被從o的等待線程池中釋放出來,并且移動到等
------------ 待線程對象o的鎖旗標的線程池中,當t得到鎖旗標時就會執行下去
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
***線程的生命控制***
class ThreadCommunation
{ public static void main(String[] args)
?? { ThreadTest tt=new ThreadTest();
???? new Thread(tt).start();
???? for(int i=0; i<100;i++)
???? { if (i==50)
?????????? tt.stopMe();? // 在第一個線程循環50次后,使第二個線程停下來
?????? System.out.println("main() is running");
???? }
?? }
}
class ThreadTest implement Runnable
{ private boolean bStop=false;
? public void stopMe()
? { bStop=false; }
? public void run()
? {? while(bStop)
???? { System.out.println(Thread.currentThread().getName()+"is running"); }
}?
---------------------------------------------
---------------------------------------------
---------------------------------------------
tt.setDaemon(true);??? Runnalbe???? synchronized(this)??????
try{tt.join(10000);} catch(Exception e){} 當主線程和子線程合并100000毫秒后,在分開運行
try{Thread.sleep(10);} catch(Exception e){}? //Thread.sleep(10)讓該線程停10毫秒,其它線程繼續執行
wait()?? notify()??
總結
以上是生活随笔為你收集整理的java学习笔记(九)----多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java学习笔记(八)----包,jar
- 下一篇: java学习笔记(十)----Strin