Java Thread
Java Thread
使用Java多線程編程很容易. Java線程總是實現(xiàn)接口java.lang.Runnable, 一般有兩種方法: 創(chuàng)建一個類實現(xiàn)接口Runnable, 創(chuàng)造該類的實例作為參數(shù)傳給Thread構(gòu)造函數(shù), 創(chuàng)造Thread實例.
packagetony.test.testJavaThread;/**
?*?@author?Tony
?*/
publicclassTestRunnable?implementsRunnable
{?
????int?count?=?0;
????
????private?synchronized?void?printHello()
????{
????????System.out.println(++count?+?".?Hello?"?+?Thread.currentThread().getName());??
????}
????
????public?void?run()
????{
????????printHello();?
????}
????
????public?static?void?main(String[]?args)
????{
????????TestRunnable?tr?=?new?TestRunnable();
????????for?(int?i=0;?i<5;?i++)
????????{
????????????new?Thread(tr,?"Tony's?Thread-"?+?(i+1)).start();
????????}
????}
}
繼承java.lang.Thread, 創(chuàng)造該類對象. 這種方法一般要重寫(Override)run方法, 不然該線程就什么也沒做就結(jié)束了.
packagetony.test.testJavaThread;/**
?*?@author?Tony
?*/
publicclassTestThread?extendsThread
{
????static?int?count?=?0;
????
????public?TestThread(String?name)
????{
????????super(name);
????}
????
????public?void?run()
????{
????????synchronized(this.getClass())
????????{
????????????System.out.println(++count?+?".?Hello?"?+?this.getName());?
????????????if?(count?==?2)
????????????{
????????????????System.out.println(this.getName()?+?"?is?to?sleep?for?4s");?
????????????????try
????????????????{
????????????????????Thread.sleep(4000);
????????????????}
????????????????catch?(InterruptedException?e)
????????????????{
????????????????????e.printStackTrace();
????????????????}
????????????}
????????}
????}
????
????public?static?void?main(String[]?args)
????{
????????for?(int?i=0;?i<5;?i++)
????????{
????????????new?TestThread("Tony's?Thread-"+(i+1)).start();
????????}
????}
}
兩者可以實現(xiàn)同樣的功能. 但個人比較喜歡前者. 由于Java是單繼承, 所以后者不能再繼承其它類了. 而前者還有個好處就是可以把線程間共享的數(shù)據(jù)作為類的字段, 然后把該類實現(xiàn)Singleton, 只實例化一個對象, 作為參數(shù)傳給Thread. 當(dāng)然如果不想共享成員, 而對于每個Thread提供不同的Runnable對象. 而后者要實現(xiàn)共享就要在繼承的類中聲明一堆static屬性.
Java Thread通過方法start啟動, 而實際運行的內(nèi)容在方法run中. 不過簡單地調(diào)用run, 如thread.run(); 只是把run作為普通的方法調(diào)用了一遍, 并沒有啟動新的線程. 當(dāng)run中的內(nèi)容運行完之后, 線程便自動結(jié)束了. 類Thread提供一個靜態(tài)方法sleep. 任何時候調(diào)用該方法都可以把當(dāng)前執(zhí)行的線程暫停指定的時間.
Java Thread同步與synchronized
先說明幾點:
1. 無論synchronized關(guān)鍵字加在方法上還是對象上, 它取得的鎖都是對象的鎖 (JDK DOC中描述為an object's monitor), 而不是指對方法或一段代碼加鎖. 加在方法上取得的鎖是該方法所屬對象的鎖, 即加在非static方法上時取得的是this的鎖, 加在static方法上時取得的是class的鎖. 這樣并行調(diào)用了一個類的多個對象加了synchronized的方法, 它們之間是沒有絲毫關(guān)系的.
2. 每個對象只有一個鎖與之相關(guān)聯(lián).而每個線程可以取得N個對象的鎖.
3. 這個鎖實際上是加在對象在堆中的地址上. 所以像基本類型等位于棧上的是不能加鎖的. 而對象引用本身也是在棧上, 所以也不會被加鎖. 這就是說像如下的代碼, 實際上兩個線程取得不是同一個鎖:
packagetony.test.testJavaThread;/**
?*?@author?p465890
?*/
publicclassTestSynchronized
{
????public?static?void?main(String[]?args)
????{
????????Runnable?rb?=?new?Runnable()
????????{
????????????//?It?is?said?that?new?Byte[0]?is?more?effective?than?new?Object()
????????????//?For?the?former?needs?two?byte?codes?and?the?later?needs?three.
????????????//?I?have?not?digged?into?it.
????????????//?Maybe?the?reason?is?as?follows.
????????????//?"Class?objects?for?array?classes?are?not?created?by?class?loaders,?but?are?created?automatically?as?required?by?the?Java?runtime.?The?class?loader?for?an?array?class,?as?returned?by?Class.getClassLoader()?is?the?same?as?the?class?loader?for?its?element?type;?if?the?element?type?is?a?primitive?type,?then?the?array?class?has?no?class?loader."
????????????byte[]?bt?=?new?byte[0];
????????????public?void?run()
????????????{
????????????????synchronized(bt)
????????????????{
????????????????????for?(int?i=0;?i<5;?i++)
????????????????????{
????????????????????????try
????????????????????????{
????????????????????????????Thread.sleep(50);
????????????????????????}
????????????????????????catch?(InterruptedException?e)
????????????????????????{
????????????????????????????e.printStackTrace();
????????????????????????}
????????????????????????bt?=?new?byte[0];
????????????????????????System.out.println(Thread.currentThread().getName());
????????????????????}
????????????????}
????????????}
????????};
????????for?(int?i=0;?i<5;?i++)
????????{
????????????try
????????????{
????????????????Thread.sleep(100);
????????????}
????????????catch?(InterruptedException?e)
????????????{
????????????????e.printStackTrace();
????????????}
????????????new?Thread(rb).start();
????????}
????}
}
4. 實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的, 所以不需要同步就不要做同步。
Wait, notify, notifyAll
這三個函數(shù)都是Object的成員方法, 也就是說所有的對象都能調(diào)用這三個函數(shù), 這與所有的對象上都有鎖是一致的, 而且這三個方法的也是對自己所屬對象鎖進(jìn)行操作, 來影響需要該對象鎖的線程的行為.
注意下面所說的new, run, schedule, wait, sleep, dead狀態(tài)并不是官方的說法, 但這樣解釋可以理解很多問題. 并且目前還沒碰到解釋不通的現(xiàn)象.
這樣說吧, 任何一個對象的鎖就好比通行證, synchronized就用來標(biāo)志某一塊程序只有用它指定的對象的通行證才能進(jìn)入. 這種通行證每個對象都有且只有一個, 而一個synchronized也能且只能指定一個對象(當(dāng)然synchronized可以嵌套, 但每一個synchronized也只能指定一個對象), 多個synchronized可以指定同一個對象, 因為對象的通行證只有一個, 所以這幾個synchronized塊中的東西就如同一塊一樣, 不能同時進(jìn)入. 但是沒有用synchronized修飾的程序自然不需要通行證, 無論哪個線程都可進(jìn)入.
一個線程創(chuàng)建好了, 但沒啟動 (沒調(diào)用start), 此時它處于new狀態(tài), 一個線程結(jié)束了就處于dead狀態(tài). 這兩種狀態(tài)很簡單, 不再多說.
對于線程來說, 要么進(jìn)入不需通行證的代碼(沒用synchronized修飾的代碼), 要么想方設(shè)法取得通行證. 但線程不像人, 它是很守規(guī)矩的. 當(dāng)一個線程想進(jìn)入synchronized塊時, 如果該synchronized指定的對象的通行證沒被其他線程使用, 它就拿走通行證進(jìn)入該synchronized塊, 注意, 一量它出來, 它一定會把通行放回原處. 此時它處于run狀態(tài). 但此時又有線程來了, 它也想進(jìn)入該synchronized塊, 它只能等待在run狀態(tài)的線程歸還通行證, 它會一直這樣耐心等待. 如果此時又有其他線程來了, 這樣就有了一個等待的隊列, 我們稱這些線程處于schedule狀態(tài), 這種狀態(tài)的線程一切都準(zhǔn)備好了, 只等著有通行證就進(jìn)入run狀態(tài). 當(dāng)然當(dāng)通行證可用時, 只有一個線程能夠得到, 其他仍然處于schedule狀態(tài). 不過到底誰有幸得到通行證, 那就由具體的JVM決定了, 程序員千萬別想著JVM會跟自己想法一樣. 我說過, 線程很守規(guī)矩. 實際上線程們還很友好. 一個處于run狀態(tài)的線程run了一會之后, 它可能想讓其他線程先run一把, 于是它就調(diào)用它擁有的通行證的對象的wait方法, 告知自己要wait了, 此時通行證歸還, 而它自己則進(jìn)入wait狀態(tài). 這個線程可以設(shè)定它將處于wait狀態(tài)的時間或者無限長(參數(shù)0時無限長, 負(fù)數(shù)會拋java.lang.IllegalArgumentException異常. 一旦時間到了, 就自動進(jìn)入schedule狀態(tài), 等待通行證. 在這個時間之內(nèi), 除非其他線程喚醒它, 否則它將一直不聞不問了, 一直wait, 實際上說sleep更確切. 怎么喚醒在下面會講.既然線程如此友好, 自然JVM也待它不薄, 一個線程wait后, 當(dāng)它重新有機(jī)會進(jìn)入schedule, 再進(jìn)入run, 它裝從wait的下一個語句開始執(zhí)行, 就好像它沒有wait過一樣. 當(dāng)很多線程都wait之后, 就有了wait隊列, 而處于run的線程, 可能會想喚醒一個或一些wait的進(jìn)程, 它可以調(diào)用擁有的通行證的對象的notify方法(喚醒一個線程, 具體哪一個, 也是JVM說了算)或者notifyAll方法(全部喚醒). 這里需要說明的是, 被喚醒的是那些處于相應(yīng)對象通行證wait隊列的線程, 其它無關(guān)的線程并不會被喚醒, 喚醒之后處于schedule狀態(tài), 此時通行證仍然為那個run狀態(tài)的線程所有, 最后到底誰能進(jìn)入run狀態(tài), 也是JVM決定的. 有時線程太好心了, 沒有某個對象的通行證, 卻硬要調(diào)用這個對象的wait, notify或notifyAll方法, 結(jié)果會拋出java.lang.IllegalMonitorStateException異常.
注意上面所講的, 各個通行證之間是獨立的, 比如, 一個線程調(diào)用一個對象的wait, 它歸還了這個對象的通行證, 但要是它還擁有其它對象的通行證, 它會仍然擁有, 當(dāng)然這可能造成死鎖. 不過這應(yīng)該是程序員的責(zé)任.
類Thread
難道所有線程都這么公平, 當(dāng)然不是. 類Thread提供了很多方法都針對某個線程進(jìn)行操作, 它要么不影響要么影響指定線程所擁有的鎖. 也就是說這些方法與上面的不同, 是針對線程而不是針對鎖的.
一個線程處于wait時, 除了上面講的等到指定時間, notify, notifyAll之外, 它還能被其他線程interrupt喚醒. interrupt()是Thread類的方法. 所以它作的是指名道姓的喚醒, 一個線程想喚醒另一個線程, 它就必須直接叫那個線程的名字, 即調(diào)用那個線程的interrupt()方法. 這個處于wait的線程就會拋出java.lang.InterruptedException異常, consume異常之后, 該線程就會進(jìn)入到schedule狀態(tài), 并且如果之前其interrupt status(這個status與前面所提的run, schedule, wait狀態(tài)沒關(guān)系, 其實更該稱之為屬性, 估計這就是Thread類的屬性, 只是JDK DOC 上這樣稱呼. 所以這里用status以示區(qū)別), 這個interrupt status會被清除. 這里要說明一下, 線程還有sleep狀態(tài), 這種狀態(tài)行為與wait基本一樣, 只是不能被nofity和notifyAll喚醒. 線程可以調(diào)用Thread的靜態(tài)方法sleep, 使當(dāng)前線程(自己)進(jìn)入sleep狀態(tài), 參數(shù)指定時間, 或Thread的成員方法join, 參數(shù)幾乎和調(diào)用wait一樣, 但它是使自己處于進(jìn)入sleep狀態(tài), 一直到被調(diào)用線程運行完了, 或者時間到了, 它才繼續(xù)運行.
packagetony.test.testJavaThread;/**
?*?@author?Tony
?*/
publicclassTestJoin?implementsRunnable
{
????int?count?=?0;
????public?static?void?main(String[]?args)
????{
????????TestJoin?tj?=?new?TestJoin();
????????for?(int?i=0;?i<5;?i++)
????????{
????????????new?Thread(tj,?"hello"+i).start();
????????}
????}
????public?void?run()
????{???
????????try
????????{
????????????Thread.sleep(1000);
????????}
????????catch?(InterruptedException?e1)
????????{
????????????e1.printStackTrace();
????????}
????????
????????if?(count?==?0)
????????{
????????????count++;
????????????Thread?thread1?=?new?Thread(this,?"helloSon");
????????????thread1.start();
????????????try
????????????{
????????????????thread1.join(3000);
????????????}
????????????catch?(InterruptedException?e)
????????????{
????????????????e.printStackTrace();
????????????}
????????}
????????System.out.println(Thread.currentThread().getName());
????}
}
只是Thread的這幾個方法顯然與擁有哪個對象的通行證沒有關(guān)系. 實際上他們擁有的對象的通行證沒有任何改變, 否則是不安全的. 實際上像stop, suspend, resume等等deplicated的方法要么調(diào)用時會釋放所有它擁有的鎖(通行證), 導(dǎo)致不安全, 要么調(diào)用之后一定要某個方法來重新啟動, 容易造成死鎖. sleep狀態(tài)和wait一樣的是都能被interrupt喚醒, 喚醒的行為一模一樣. 如果一個線程沒有處于wait和sleep狀態(tài), 并且也沒有” blocked in an I/O operation upon an interruptible channel”或”blocked in a Selector”, 它的interrupt status會被設(shè)置. Thread同時提供了兩個方法來查詢一個進(jìn)程的interrupt status: 一種是靜態(tài)方法interrupted()查詢當(dāng)前進(jìn)程的interrupt status, 返回boolean值, 同時清除interrupt status; 另一種是成員方法isInterrupted(), 但它不會清除interrupt status. 所以要想查詢當(dāng)前線程interrupt status又不想清除之, 可以這樣: Thread.currentThread.isInterrupted().
前面所說的一開始線程的interrupt status都為false, 如果一開始interrupt status就被設(shè)為true, 那么該線程一調(diào)用wait, sleep或join就會拋出java.lang.InterruptedException異常, 并且interrupt status被清除. 實際上我們?nèi)绻裪nterrupt()的調(diào)用看到先設(shè)置interrupt status, 接下來的行為就一致了, 而不管是先設(shè)interrupt status, 還是先進(jìn)為wait或sleep狀態(tài).
packagetony.test.testJavaThread;/**
?*?@author?Tony
?*/
publicclassTestInterrupt?implementsRunnable
{?
????int?count?=?0;
????
????private?synchronized?void?printHello()
????{
????????System.out.println(++count?+?".?Hello?"?+?Thread.currentThread().getName());
????????System.out.println(Thread.currentThread().getName()+":?"+Thread.currentThread().isInterrupted());
????????if?(count?==?1)
????????{
????????????try
????????????{
????????????????wait();
????????????}
????????????catch?(InterruptedException?e)
????????????{
????????????????System.out.println(Thread.currentThread().getName()+":?"+Thread.currentThread().isInterrupted());
????????????????e.printStackTrace();
????????????}
????????????System.out.println(Thread.currentThread().getName()?+?"?is?back");
????????}
????????System.out.println(Thread.currentThread().getName()+":?"+Thread.currentThread().isInterrupted());
????}
????
????public?void?run()
????{
????????printHello();?
????}
????
????public?static?void?main(String[]?args)
????{
????????TestInterrupt?tr?=?new?TestInterrupt();
????????Thread[]?threads?=?new?Thread[5];
????????for?(int?i=0;?i<5;?i++)
????????{
????????????threads[i]?=?new?Thread(tr,?"Tony's?Thread-"?+?(i+1));
????????????threads[i].start();
????????????threads[i].interrupt();
????????}
????????
????????try
????????{
????????????Thread.sleep(2000);
????????}
????????catch?(InterruptedException?e)
????????{
????????????e.printStackTrace();
????????}
????????System.out.println(Thread.currentThread().getName()+":?"+Thread.interrupted());
????????threads[0].interrupt();
????}
}
線程們之間友好還體現(xiàn)在Thread靜態(tài)方法yield(), 它所做的是讓自己暫停一下. 實際上就相當(dāng)于把自己的狀態(tài)從run變到schedule, 重新競爭通行證(鎖), 最后到底誰會進(jìn)入run, 或者它自己又進(jìn)入了run, 由JVM決定.
線程可以設(shè)置或查詢優(yōu)先級, 高優(yōu)先級進(jìn)程優(yōu)先. 線程還能設(shè)置或查詢是否成為Daemon線程. Daemon線程和非Daemon線程的區(qū)別是JVM對它們的態(tài)度不同. 只要沒有非Daemon線程存在, JVM就退出, 就像調(diào)用了Sysem.exit()一樣, 而不管到底有沒有或有多少Daemon線程仍在運行.
類ThreadGroup
至于ThreadGroup, 它所做的就是把線程分組來管理, 比如, 調(diào)用ThreadGroup的interrupt方法, 相當(dāng)于調(diào)用組內(nèi)所有線程的interrupt方法. 組是樹裝結(jié)構(gòu)的, 樹的根結(jié)點組的名稱是”system”. 如果不指定組名, 創(chuàng)建的Thread或ThreadGroup就是當(dāng)前組的子結(jié)點. Java程序main方法所在的組名為”main”, 是”system”的子結(jié)點, 調(diào)用ThreadGroup的list方法就可以把從該組開始的樹狀結(jié)構(gòu)比文本形式打印出來.
ThreadGroup和Thread的name都可以一樣.
packagetony.test.testJavaThread;/**
?*?@author?p465890
?*/
publicclassTestThreadGroup
{
????public?static?void?main(String[]?args)
????{
????????ThreadGroup?tg?=?new?ThreadGroup("tg-hello");
????????new?Thread(tg,?new?Runnable()
????????{
????????????public?void?run()
????????????{????????????????
????????????}
????????},?"th-world");
????????ThreadGroup?tg2?=?new?ThreadGroup("tg-hello");
????????
????????tg.getParent().getParent().list();
????}
}
ThreadGroup提供了一個處理非Catch異常的方法, 只要重載ThreadGroup中的uncaughtException方法, 屬于該組的Thread只要拋了非Catch異常, 就會調(diào)用此方法.
packagetony.test.testJavaThread;/**
?*?@Tony
?*/
publicclassTestUncaughtException?extendsThreadGroup
{
????public?TestUncaughtException(String?name)
????{
????????super(name);
????}
????
????public?void?uncaughtException(Thread?t,?Throwable?e)
????{
????????System.out.println(e.getClass().getName()?+?"?occurs?in?thread?"?+?t.getName());
????????e.printStackTrace();
????}
????
????public?static?void?main(String[]?args)
????{
????????new?Thread(new?TestUncaughtException("testUncaughtException"),?new?Runnable()
????????????{
????????????????public?void?run()
????????????????{
????????????????????System.out.println("Test?uncaughtException:?1/0");
????????????????????int?i?=?1?/?0;
????????????????????System.out.println("Test?uncaughtException?ends");
????????????????}
????????????}).start();
????}
}
對于JDK1.5以止版本, Thread本身就提供了setUncaughtExceptionHandler, 在線程粒度處理非Catch異常. JDK DOC中描述如下:
?“Uncaught exception handling is controlled first by the thread, then by the thread's ThreadGroup object and finally by the default uncaught exception handler. If the thread does not have an explicit uncaught exception handler set, and the thread's thread group (including parent thread groups) does not specialize its uncaughtException method, then the default handler's uncaughtException method will be invoked. “
packagetony.testJavaThread;publicclassTestUncatchedExceptionHandler
{
????public?static?void?main(String[]?args)
????{
????????Thread.currentThread().setUncaughtExceptionHandler(new?Thread.UncaughtExceptionHandler()
????????????{
????????????????public?void?uncaughtException(Thread?t,?Throwable?e)??
????????????????{
????????????????????System.out.println(e.getClass().getName()?+?"?occurs?in?thread?"?+?t.getName());
????????????????????e.printStackTrace();
????????????????}
????????????});
????????int?i?=?1?/?0;
????}
}
Thread還提供方法getContextClassLoader, 得到該線程的ClassLoader對象.
原文:http://blog.csdn.net/tonywjd/archive/2007/01/15/1483958.aspx
總結(jié)
以上是生活随笔為你收集整理的Java Thread的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解Java多态性
- 下一篇: replace使用案例--替换空格