版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1.JAVA的StringBuffer類
StringBuffer類和String一樣,也用來(lái)代表字符串,只是由于StringBuffer
的內(nèi)部實(shí)現(xiàn)方式和String不同,所以StringBuffer在進(jìn)行字符串處理時(shí),不生
成新的對(duì)象,在內(nèi)存使用上要優(yōu)于String類。所以在實(shí)際使用時(shí),
如果經(jīng)常需要對(duì)一個(gè)字符串進(jìn)行修改,例如插入、刪除等操作,使用
StringBuffer要更加適合一些。
在StringBuffer類中存在很多和String類一樣的方法,這些方法在功
能上和String類中的功能是完全一樣的。
但是有一個(gè)最顯著的區(qū)別在于,對(duì)于StringBuffer對(duì)象的每次修改都會(huì)改變對(duì)
象自身,這點(diǎn)是和String類最大的區(qū)別。
另外由于StringBuffer是線程安全的,關(guān)于線程的概念后續(xù)有專門的
章節(jié)進(jìn)行介紹,所以在多線程程序中也可以很方便的進(jìn)行使用,但是程序的
執(zhí)行效率相對(duì)來(lái)說(shuō)就要稍微慢一些。
1、StringBuffer對(duì)象的初始化
StringBuffer對(duì)象的初始化不像String類的初始化一樣,Java提供的有特殊的
語(yǔ)法,而通常情況下一般使用構(gòu)造方法進(jìn)行初始化。
例如:
StringBuffers=newStringBuffer();
這樣初始化出的StringBuffer對(duì)象是一個(gè)空的對(duì)象。
如果需要?jiǎng)?chuàng)建帶有內(nèi)容的StringBuffer對(duì)象,則可以使用:
StringBuffers=newStringBuffer("abc’');
這樣初始化出的StringBuffer對(duì)象的內(nèi)容就是字符串“abc”。
需要注意的是,StringBuffer和String屬于不同的類型,也不能直接進(jìn)行強(qiáng)制
類型轉(zhuǎn)換,下面的代碼都是錯(cuò)誤的:
StringBuffers="abc”;〃賦值類型不匹配
StringBuffers=(StringBuffer)^^abc^^;〃不存在繼承關(guān)系,無(wú)法進(jìn)
行強(qiáng)轉(zhuǎn)
StringBuffer對(duì)象和String對(duì)象之間的互轉(zhuǎn)的代碼如下:
Strings="abc”;
StringBuffersbl=newStringBuffer(“123”);
StringBuffersb2=newStringBuffer(s);//String轉(zhuǎn)換為StringBuffer
Stringsi=sbl.toString();//StringBuffer轉(zhuǎn)換為String
2、StringBuffer的常用方法
StringBuffer類中的方法主要偏重于對(duì)于字符串的變化,例如追加、插入和刪
除等,這個(gè)也是StringBuffer和String類的主要區(qū)別。
a>append方法
publicStringBufferappend(boolear)b)
該方法的作用是追加內(nèi)容到當(dāng)前StringBuffer對(duì)象的末尾,類似于字符串的連
接。調(diào)用該方法以后,StringBuffer對(duì)象的內(nèi)容也發(fā)生改變,例如:
StringBuffersb=newStringBuffer(''abc");
sb.append(true);
則對(duì)象sb的值將變成“abctrue”。
使用該方法進(jìn)行字符串的連接,將比String更加節(jié)約內(nèi)容,例如應(yīng)用于數(shù)據(jù)
庫(kù)SQL語(yǔ)句的連接,例如:
StringBuffersb=newStringBuffer();
Stringuser="test”;
Stringpwd="123”;
sb.append(44select*fromuserinfowhereusername=")
.append(user)
.append^andpwd=")
.append(pwd);
這樣對(duì)象sb的值就是字符串“select*fromuserinfowhere
username=testandpwd=123n。
b>deleteCharAt方法
publicStringBufferdeleteCharAt(intindex)
該方法的作用是刪除指定位置的字符,然后將剩余的內(nèi)容形成新的字符串。
例如:
StringBuffersb=newStringBufferC4Tesf,);
sb.deleteCharAt(l);
該代碼的作用刪除字符串對(duì)象sb中索引值為1的字符,也就是刪除第二個(gè)字
符,剩余的內(nèi)容組成一個(gè)新的字符串。所以對(duì)象sb的值變?yōu)?Tst”。
還存在一個(gè)功能類似的delete方法:
publicStringBufferdelete(intstartjntend)
該方法的作用是刪除指定區(qū)間以內(nèi)的所有字符,包含start,不包含end索引
值的區(qū)間。例如:
StringBuffersb=newStringBuffer(64TestString,,J;
sb.delete(1,4);
該代碼的作用是刪除索引值1(包括)到索引值4(不包括)之間的所有字符,剩余
的字符形成新的字符串。則對(duì)象sb的值是“TString)
c、insert方法
publicStringBufferinsert(intoffset,booleanb)
該方法的作用是在StringBuffer對(duì)象中插入內(nèi)
容,然后形成新的字符串。例如:
StringBuffersb=new
StringBuffer("TestString");
sb.insert(4,false);
該示例代碼的作用是在對(duì)象sb的索引值4的位置插入false值,形成新的字符
串,則執(zhí)行以后對(duì)象sb的值是"TestfalseString”。
d、reverse方法
publicStringBufferreverse()
該方法的作用是將StringBuffer對(duì)象中的內(nèi)容反轉(zhuǎn),然后形成新的字符串。例
如:
StringBuffersb=newStringBuffer("abc");
sb.reverse();
經(jīng)過(guò)反轉(zhuǎn)以后,對(duì)象sb中的內(nèi)容將變?yōu)?cba\
e^setCharAt方法
publicvoidsetCharAt(intindex,charch)
該方法的作用是修改對(duì)象中索引值為index位
置的字符為新的字符ch。例如:
StringBuffersb=new
StringBuffer(“abc”);
sb-setCharAtfl/D5);
則對(duì)象sb的值將變成“aDc”。
f、trimToSize方法
publicvoidtrimToSize()
該方法的作用是將StringBuffer對(duì)象的中存儲(chǔ)空間縮小到和字符串長(zhǎng)度一樣的
長(zhǎng)度,減少空間的浪費(fèi)。
總之,在實(shí)際使用時(shí),String和StringBuffer各有優(yōu)勢(shì)和不足,可以
根據(jù)具體的使用環(huán)境,選擇對(duì)應(yīng)的類型進(jìn)行使用。
2.Java中replace。、replaceFirst()和replaceAII()區(qū)別
str.replace(str中被替換的,替換后的字符)
replace和replaceAII是JAVA中常用的替換字符的方法,它們的區(qū)別是:
1)replace的參數(shù)是char和CharSequence,即可以支持字符的替換,也支持字符串的替換
(CharSequence即字符串序列的意思,說(shuō)白了也就是字符串);
2)replaceAII的參數(shù)是regex,即基于規(guī)則表達(dá)式的替換,比如,可以通過(guò)replaceAII("\\d",
“*")把?個(gè)字符串所有的數(shù)字字符都換成星號(hào);
相同點(diǎn)是都是全部替換,即把源字符串中的某一字符或字符串全部換成指定的字符或字
符串,如果只想替換第一次出現(xiàn)的,可以使用replaceHrst。,這個(gè)方法也是基于規(guī)則表達(dá)式的
替換,但與replaceAII。不同的是,只替換第一次出現(xiàn)的字符串;
另外,如果replaceAII。和replaceFirst。所用的參數(shù)據(jù)不是基于規(guī)則表達(dá)式的,則與
replace。替換字符串的效果是一樣的,
即這兩者也支健待串的操作;
還有一點(diǎn)注意:
執(zhí)行了替換操作后,源字符串的內(nèi)容是沒(méi)有發(fā)生改變的(因?yàn)镾tring類是final類型的不可
改寫,但可以把處理得到的結(jié)果賦值).
舉例如下:
Stringsrc=newString,'ab4332c43d");
System.out.println(src.replace(n3',,"f"));=>ab4f2c4fd.
System.out.println(src.replace(,3',,f'));=>ab4f2c4fd.
System.out.println(src.replaceAII("\\d",,,f,'));=>abffafcffd.
,,
System.out.println(src.replaceAII("a'J'f"));=>fb43fc23d.
System.out.println(src.replaceFirst(',\\d,"f',));=>abf32c43d
System.out.println(src.replaceFirst(',4",,'h"));=>abh32c43d.
如何將字符串中的“'“替換成“\\":
Stringmsgln;
StringmsgOut;
,,,w
msgOut=msgln.replaceAII('\\\\J"\\\\\\\\);
原因:
'在java中是一個(gè)轉(zhuǎn)義字符,所以需要用兩個(gè)代表一個(gè)。例如System.out.println("\\");
只打印出個(gè)
但是'''也是正則表達(dá)式中的轉(zhuǎn)義字符(replaceAII的參數(shù)就是正則表達(dá)式),需要用兩個(gè)代
表一個(gè)。所以:\\\\被java轉(zhuǎn)換成\\八\又被正則表達(dá)式轉(zhuǎn)換成\。
同樣
CODE:\\\\\\\\
Java:\\\\
Regex:\\
將字符串中的'/‘替換成'\'的幾種方式:
msgOut=msgln.replaceAII('7","\\\\");
msgOut=msgln.replace('7","\\");
3.window.location和window.open的區(qū)另
window.location=""跳轉(zhuǎn)后有后退功能
window.location.replace("")跳轉(zhuǎn)后沒(méi)有后退功
能
window.open("")要新的窗口打開(kāi)鏈接
4.Class.forName的作用以及為什么要用它【轉(zhuǎn)】
Fbstedon2010-03-0310:24火之光閱讀(674)評(píng)論(0)編輯收藏.
Qass.forName(xxx.xx.xx)返回的是-,個(gè)類
首先你要明白在java里面任何class都要裝載在虛擬機(jī)上才能運(yùn)行。這句話就是裝載類用的(和new
不一樣,要分清楚)。
至于什么時(shí)候用,你可以考慮一下這個(gè)問(wèn)題,給你一個(gè)字符串變量,它代表一個(gè)類的包名和類名,你
怎么實(shí)例化它?只有你提到的這個(gè)方法了,不過(guò)要再加一點(diǎn)。
Aa=(A)Class.forName("pacage.A").newlnstance();
這和你
Aa=newA();
是一樣的效果。
關(guān)于補(bǔ)充的問(wèn)題
答案是肯定的,jvm會(huì)執(zhí)行靜態(tài)代碼段,你要記住一個(gè)概念,靜態(tài)代碼是和class綁定的,class裝載
成功就表示執(zhí)行了你的靜態(tài)代碼了。而且以后不會(huì)再走這段靜態(tài)代碼了。
Qass.forName(xxx.xx.xx)返回的是-,個(gè)類
Qass.forName(xxx.xx.xx);的作用是要求JVM查找并加載指定的類,也就是說(shuō)JVM會(huì)執(zhí)行該類的靜態(tài)
代碼段
動(dòng)態(tài)加載和創(chuàng)建Qass對(duì)象,比如想根據(jù)用戶輸入的字符串來(lái)創(chuàng)建對(duì)象
Stringstr=用戶輸入的字符串
Classt=Qass.forName(str);
t.newlnstance();
在初始化一個(gè)類,生成?個(gè)實(shí)例的時(shí)候,newlnstance。方法和new關(guān)鍵字除了一個(gè)是方法,一個(gè)是
關(guān)鍵字外,最主要有什么區(qū)別?它們的區(qū)別在于創(chuàng)建對(duì)象的方式不一樣,前者是使用類加載機(jī)制,后
者是創(chuàng)建一個(gè)新類。那么為什么會(huì)有兩種創(chuàng)建對(duì)象方式?這主要考慮到軟件的可伸縮、可擴(kuò)展和可重
用等軟件設(shè)計(jì)思想。
Java中工廠模式經(jīng)常使用newlnstance()方法來(lái)創(chuàng)建對(duì)象,因此從為什么要使用工廠模式上可以找到
具體答案。例如:
classc=Qass.forName("Example");
factory=(Examplelnterface)c.newlnstance();
其中Exampieinterface是Example的接口,可以寫成如下形式:
StringclassName="Example";
classc=Qass.forName(className);
factory=(Examplelnterface)c.newlnstance();
進(jìn)一步可以寫成如下形式:
StringclassName=readfromXMlConfig;〃從xml配置文件中獲得字符串
classc=aass.forName(className);
factory=(Examplelnterface)c.newlnstance();
上面代碼已經(jīng)不存在Example的類名稱,它的優(yōu)點(diǎn)是,無(wú)論Example類怎么變化,上述代碼不變,
甚至可以更換Example的兄弟類Example2,Examples,Example4......,只要他們繼承
Exampleinterface就可以。
從JVM的角度看,我們使用關(guān)鍵字new創(chuàng)建一個(gè)類的時(shí)候,這個(gè)類可以沒(méi)有被加載。但是使用
newlnstance()方法的時(shí)候,就必須保證:1、這個(gè)類已經(jīng)加載;2、這個(gè)類已經(jīng)連接了。而完成上面
兩個(gè)步驟的正是Qass的靜態(tài)方法forName()所完成的,這個(gè)靜態(tài)方法調(diào)用了啟動(dòng)類加教器,即加載
javaAPI的那個(gè)加載器。
現(xiàn)在可以看出,newlnstance()實(shí)際上是把new這個(gè)方式分解為兩步,即首先調(diào)用Class加載方法加
載某個(gè)類,然后實(shí)例化。這樣分步的好處是顯而易見(jiàn)的。我們可以在調(diào)用class的靜態(tài)加載方法
forName時(shí)獲得更好的靈活性,提供給了一種降耦的手段。
最后用最簡(jiǎn)單的描述來(lái)區(qū)分new關(guān)鍵字和newlnstance。方法的區(qū)別:
newlnstance:弱類型。低效率。只能調(diào)用無(wú)參構(gòu)造。
new:強(qiáng)類型。相對(duì)高效。能調(diào)用任何public構(gòu)造。
5.Hibernate包作用詳解【轉(zhuǎn)】
Fastedon2009-12-2311:32火之光閱讀(168)評(píng)論(0)編輯收藏.
Hibernate?共包括了23個(gè)jar包,令人眼花繚亂。本文將詳細(xì)講解Hibernate每個(gè)jar包的作用,便于
你在應(yīng)用中根據(jù)自己的需要進(jìn)行取舍。
下載Hibernate,例如2.0.3穩(wěn)定版本,解壓縮,可以看到一個(gè)hibernate2.jar和lib目錄下有22個(gè)jar
包:
?hibernate2.jar:
Hibernate的庫(kù),沒(méi)有什么可說(shuō)的,必須使用的jar包
?cglib-asm.jar:
CGLIB庫(kù),Hibernate用它來(lái)實(shí)現(xiàn)P0字節(jié)碼的動(dòng)態(tài)生成,非常核心的庫(kù),必須使用的jar包
?dom4j.jar:
dom4j是一個(gè)Java的XMLAPI,類似于jdom,用來(lái)讀寫XML文件的。dom4j是一個(gè)非常非常優(yōu)秀的JavaXML
API,具有性能優(yōu)異、功能強(qiáng)大和極端易用使用的特點(diǎn),同時(shí)它也是一個(gè)開(kāi)放源代碼的軟件,可以在
SourceForge上找到它。在IBMdeveloperWorks上面可以找到一篇文章,對(duì)主流的JavaXMLAPI進(jìn)行的
性能、功能和易用性的評(píng)測(cè),dom4j無(wú)論在那個(gè)方面都是非常出色的。我早在將近兩年之前就開(kāi)始使用
doin4j,直到現(xiàn)在。如今你可以看到越來(lái)越多的Java軟件都在使用doiMj來(lái)讀寫XML,特別值得一提的是
連Sun的JAXM也在用dom4jo這是必須使用的jar包,Hibernate用它來(lái)讀寫配置文件。
?odmg.jar:
0DMG是?個(gè)ORM的規(guī)范,Hibernate實(shí)現(xiàn)了ODMG規(guī)范,這是?個(gè)核心的庫(kù),必須使用的jar包。
?commons-collections,jar:
ApacheCommons包中的一個(gè),包含了一些Apache開(kāi)發(fā)的集合類,功能比java,uti1.*強(qiáng)大。必須使用的
jar包。
?commons-beanutils.jar:
ApacheCommons包中的一個(gè),包含了一些Bean_L具類類。必須使用的jar包。
?commons-lang,jar:
ApacheCommons包中的一個(gè),包含了一些數(shù)據(jù)類型工具類,是java.lang.*的擴(kuò)展。必須使用的jar包。
?commons-logging,jar:
ApacheCommons包中的一個(gè),包含了日志功能,必須使用的jar包。這個(gè)包本身包含了一個(gè)SimpleLogger,
但是功能很弱。在運(yùn)行的時(shí)候它會(huì)先在CLASSPATH找log4j,如果有,就使用log4j,如果沒(méi)有,就找JDKL4
帶的java.util,logging,如果也找不到就用SimpleLoggerocommons-logging,jar的出現(xiàn)是一個(gè)歷史的
的遺留的遺憾,當(dāng)初Apache極力游說(shuō)Sun把log4j加入JDK1.4,然而JDKL4項(xiàng)目小組已經(jīng)接近發(fā)布JDK1.4
產(chǎn)品的時(shí)間了,因此拒絕了Apache的要求,使用自己的java.util,logging,這個(gè)包的功能比log4j差的
很遠(yuǎn),性能也一般。后來(lái)Apache就開(kāi)發(fā)出來(lái)了commons-logging,jar用來(lái)兼容兩個(gè)logger。因此用
commons-logging,jar寫的log程序,底層的Logger是可以切換的,你可以選擇log4j,java.util,logging
或者它自帶的SimpleLogger0不過(guò)我仍然強(qiáng)烈建議使用log4j,因?yàn)閘og4j性能很高,log輸出信息時(shí)間
幾乎等于System,oul,而處理一條log平均只需要5us。你可以在Hibernate的src目錄卜找到Hibernate
已經(jīng)為你準(zhǔn)備好了的log4j的配置文件,你只需要到Apache網(wǎng)站去下載log4j就可以了。
commons-logging.jar也是必須的jar包。
使用Hibernate必須的jar包就是以上的這兒個(gè),剩下的都是可選的。
?ant.jar:
Ant編譯工具的jar包,用來(lái)編譯Hibernate源代碼的。如果你不準(zhǔn)備修改和編譯Hibernate源代碼,那
么就沒(méi)有什么用,可選的jar包
?optional,jar:
Ant的一個(gè)輔助包。
?c3p0.jar:
C3Po是?個(gè)數(shù)據(jù)庫(kù)連接池,Hibernate可以配置為使用C3Po連接池。如果你準(zhǔn)備用這個(gè)連接池,就需要這
個(gè)jar包。
?proxool.jar:
也是一個(gè)連接池,同上。
?commons-pool,jar,commons-dbcp.jar:
DBCP數(shù)據(jù)庫(kù)連接池,Apache的Jakarta組織開(kāi)發(fā)的,Tomcal4的連接池也是DBCP。
實(shí)際上Hibernate自己也實(shí)現(xiàn)了一個(gè)非常非常簡(jiǎn)單的數(shù)據(jù)庫(kù)連接池,加上上面3個(gè),你實(shí)際上可以在
Hibernate上選擇4種不同的數(shù)據(jù)庫(kù)連接池,選擇哪?個(gè)看個(gè)人的偏好,不過(guò)DBCP可能更通用?些。另外
強(qiáng)調(diào)一點(diǎn),如果在EJB中使用Hibernate,一定要用AppServer的連接池,不要用以上4種連接池,否則
容器管理事務(wù)不起作用。
?connector,jar:
JCA規(guī)范,如果你在AppServer上把Hibernate配置為Connector的話,就需要這個(gè)jar。不過(guò)實(shí)際上一
般AppServer肯定會(huì)帶上這個(gè)包,所以實(shí)際上是多余的包。
?jaas.jar:
JAAS是用來(lái)進(jìn)行權(quán)限驗(yàn)證的,已經(jīng)包含在JDK1.4里面了。所以實(shí)際上是多余的包。
?jcs.jar:
如果你準(zhǔn)備在Hibernate中使用JCS的話,那么必須包括它,否則就不用。
?jdbc2_0-stdext.jar:
JDBC2.0的擴(kuò)展包,一般來(lái)說(shuō)數(shù)據(jù)庫(kù)連接池會(huì)用上它。不過(guò)AppServer都會(huì)帶匕所以也是多余的。
?jta.jar:
JTA規(guī)范,當(dāng)Hibernate使用JTA的時(shí)候需要,不過(guò)AppServer都會(huì)帶上,所以也是多余的。
?junit.jar:
Junit包,當(dāng)你運(yùn)行Hibernate自帶的測(cè)試代碼的時(shí)候需要,否則就不用。
?xalan.jar,xerces.jar,xml-apis.jar:
Xerces是XML解析器,Xalan是格式化器,xml-apis實(shí)際上是JAXP。一般AppServer都會(huì)帶上,JDKL4
也包含了解析器,不過(guò)不是Xerces,是Crimson,效率比較差,不過(guò)Hibemate用XML只不過(guò)是讀取配置
文件,性能沒(méi)什么緊要的,所以也是多余的。
6.JAVA多線程的問(wèn)題以及處理【轉(zhuǎn)】
Restedon2009-12-0317:43火之光閱讀(603)評(píng)論(1)編輯收藏.
124多線程問(wèn)題及處理
多線程編程為程序開(kāi)發(fā)帶來(lái)了很多的方便,但是也帶來(lái)了一些問(wèn)題,
這些問(wèn)題是在程序開(kāi)發(fā)過(guò)程中必須進(jìn)行處理的問(wèn)題。
這些問(wèn)題的核心是,如果多個(gè)線程同時(shí)訪問(wèn)一個(gè)資源,例如變量、
文件等,時(shí)如何保證訪問(wèn)安全的問(wèn)題。在多線程編程中,這種會(huì)被多個(gè)線程
同時(shí)訪問(wèn)的資源叫做臨界資源。
下面通過(guò)一個(gè)簡(jiǎn)單的示例,演示多個(gè)線程訪問(wèn)臨界資源時(shí)產(chǎn)生的問(wèn)
題。在該示例中,啟動(dòng)了兩個(gè)線程類DataThread的對(duì)象,該線程每隔200毫
秒輸出一次變量n的值,并將n的值減少1。變量n的值存儲(chǔ)在模擬臨界資源
的Data類中,該示例的核心是兩個(gè)線程類都使用同一個(gè)Data類的對(duì)象,這
樣Data類的這個(gè)對(duì)象就是一個(gè)臨界資源了。示例代碼如下:
packagesynl;
/**
*模擬臨界資源的類
*/
publicclassData{
publicintn;
publicData(){
n=60;
)
}
packagesynl;
/**
*測(cè)試多線程訪問(wèn)時(shí)的問(wèn)題
*/
publicclassTestMulThreadl{
publicstaticvoidmain(String[]args){
Datadata=newData();
DataThreaddl=newDataThread(dataJ線程:T);
DataThreadd2=newDataThread(dataJ線程2");
)
)
packagesynl;
/**
*訪問(wèn)數(shù)據(jù)的線程
*/
publicclassDataThreadextendsThread{
Datadata;
Stringname;
publicDataThread(Datadata,Stringname){
this.data=data;
=name;
start();
)
publicvoidrun(){
try(
for(inti=0;i<10;i++){
System.out.printin(name+
":"+data.n);
data.n-;
Thread.sleep(200);
)
}catch(Exceptione){}
)
)
在運(yùn)行時(shí),因?yàn)椴煌闆r下該程序的運(yùn)行結(jié)果會(huì)出現(xiàn)不同,該程序
的一種執(zhí)行結(jié)果為:
線程1:60
線程2:60
線程2:58
線程1:58
線程2:56
線程1:56
線程2:54
線程1:54
線程2:52
線程1:52
線程2:50
線程1:50
線程2:48
線程1:48
線程2:47
線程1:46
線程2:44
線程1:44
線程2:42
線程1:42
從執(zhí)行結(jié)果來(lái)看,第一次都輸出60是可以理解的,因?yàn)榫€程在執(zhí)行
時(shí)首先輸出變量的值,這個(gè)時(shí)候變量n的值還是初始值60,而后續(xù)的輸出就
比較麻煩了,在開(kāi)始的時(shí)候兩個(gè)變量保持一致的輸出,而不是依次輸出n的
每個(gè)值的內(nèi)容,而到將要結(jié)束時(shí),線程2輸出47這個(gè)中間數(shù)值。
出現(xiàn)這種結(jié)果的原因很簡(jiǎn)單:線程1改變了變量n的值以后,還沒(méi)有
來(lái)得及輸出,這個(gè)變量n的值就被線程2給改變了,所以在輸出時(shí)看的輸出都
是跳躍的,偶爾出現(xiàn)了連續(xù)。
出現(xiàn)這個(gè)問(wèn)題也比較容易接受,因?yàn)樽罨镜亩嗑€程程序,系統(tǒng)只
保證線程同時(shí)執(zhí)行,至于哪個(gè)先執(zhí)行,哪個(gè)后執(zhí)行,或者執(zhí)行中會(huì)出現(xiàn)一個(gè)
線程執(zhí)行到一半,就把CPU的執(zhí)行權(quán)交給了另外一個(gè)線程,這樣線程的執(zhí)行
順序是隨機(jī)的,不受控制的。所以會(huì)出現(xiàn)上面的結(jié)果。
這種結(jié)果在很多實(shí)際應(yīng)用中是不能被接受的,例如銀行的應(yīng)用,兩
個(gè)人同時(shí)取一個(gè)賬戶的存款,一個(gè)使用存折、一個(gè)使用卡,這樣訪問(wèn)賬戶的
金額就會(huì)出現(xiàn)問(wèn)題?;蛘呤鞘燮毕到y(tǒng)中,如果也這樣就出現(xiàn)有人買到相同座
位的票,而有些座位的票卻未售出。
在多線程編程中,這個(gè)是一個(gè)典型的臨界資源問(wèn)題,解決這個(gè)問(wèn)題
最基本,最簡(jiǎn)單的思路就是使用同步關(guān)鍵字synchronized□
synchronized關(guān)鍵字是一個(gè)修飾符,可以修飾方法或代碼塊,其的
作用就是,對(duì)于同一個(gè)對(duì)象(不是一個(gè)類的不同對(duì)象),當(dāng)多個(gè)線程都同時(shí)調(diào)
用該方法或代碼塊時(shí),必須依次執(zhí)行,也就是說(shuō),如果兩個(gè)或兩個(gè)以上的線
程同時(shí)執(zhí)行該段代碼時(shí),如果一個(gè)線程已經(jīng)開(kāi)始執(zhí)行該段代碼,則另外一個(gè)
線程必須等待這個(gè)線程執(zhí)行完這段代碼才能開(kāi)始執(zhí)行。就和在銀行的柜臺(tái)辦
理業(yè)務(wù)一樣,營(yíng)業(yè)員就是這個(gè)對(duì)象,每個(gè)顧客就好比線程,當(dāng)一個(gè)顧客開(kāi)始
辦理時(shí),其它顧客都必須等待,及口寸這個(gè)正在辦理的顧客在辦理過(guò)程中接了
一個(gè)電話(類比于這個(gè)線程釋放了占用CPU的時(shí)間,而處于阻塞狀態(tài)),其它
線程也只能等待。
使用synchronized關(guān)鍵字修改以后的上面的代碼為:
packagesyn2;
/**
*模擬臨界資源的類
*/
publicclassData2{
publicintn;
publicData2(){
n=60;
)
publicsynchronizedvoidaction(Stringname){
System.out.println(name++n);
n-;
)
)
packagesyn2;
/**
*測(cè)試多線程訪問(wèn)時(shí)的問(wèn)題
*/
publicclassTestMulThread2{
publicstaticvoidmain(String[]args){
Data2data=newData2();
Data2Threaddl=newData2Thread(data,"線程
1");
Data2Threadd2=newData2Thread(data,"線程
2");
)
)
packagesyn2;
/**
*訪問(wèn)數(shù)據(jù)的線程
*/
publicclassData2ThreadextendsThread{
Data2data;
Stringname;
publicData2Thread(Data2data,Stringname){
this.data=data;
=name;
start();
)
publicvoidrun(){
try(
for(inti=O;i<10;i++){
data.action(name);
Thread.sleep(200);
)
}catch(Exceptione){}
)
}
該示例代碼的執(zhí)行結(jié)果會(huì)出現(xiàn)不同,一種執(zhí)行結(jié)果為:
線程1:60
線程2:59
線程2:58
線程1:57
線程2:56
線程1:55
線程2:54
線程1:53
線程2:52
線程1:51
線程2:50
線程1:49
線程1:48
線程2:47
線程2:46
線程1:45
線程2:44
線程1:43
線程2:42
線程1:41
在該示例中,將打印變量n的代碼和變量n變化的代碼組成一個(gè)專
門的方法action,并且使用修飾符synchronized修改該方法,也就是說(shuō)對(duì)于一
個(gè)Data2的對(duì)象,無(wú)論多少個(gè)線程同時(shí)調(diào)用action方法時(shí),只有一個(gè)線程完全
執(zhí)行完該方法以后,別的線程才能夠執(zhí)行該方法。這就相當(dāng)于一個(gè)線程執(zhí)行
到該對(duì)象的synchronized方法時(shí),就為這個(gè)對(duì)象加上了一把鎖,鎖住了這個(gè)
對(duì)象,別的線程在調(diào)用該方法時(shí),發(fā)現(xiàn)了這把鎖以后就繼續(xù)等待下去了。
如果這個(gè)例子還不能幫助你理解如何解決多線程的問(wèn)題,那么下面再來(lái)看
一個(gè)更加實(shí)際的例子——衛(wèi)生間問(wèn)題。
例如火車上車廂的衛(wèi)生間,為了簡(jiǎn)單,這里只模擬一個(gè)衛(wèi)生間,這
個(gè)衛(wèi)生間會(huì)被多個(gè)人同時(shí)使用,在實(shí)際使用時(shí),當(dāng)一個(gè)人進(jìn)入衛(wèi)生間時(shí)則會(huì)
把衛(wèi)生間鎖上,等出來(lái)時(shí)打開(kāi)門,下一個(gè)人進(jìn)去把門鎖上,如果有一個(gè)人在
衛(wèi)生間內(nèi)部則別人的人發(fā)現(xiàn)門是鎖的則只能在外面等待。從編程的角度來(lái)看,
這里的每個(gè)人都可以看作是一個(gè)線程對(duì)象,而這個(gè)衛(wèi)生間對(duì)象由于被多個(gè)線
程訪問(wèn),則就是臨界資源,在一個(gè)線程實(shí)際使用時(shí),使用synchronized關(guān)鍵
將臨界資源鎖定,當(dāng)結(jié)束時(shí),釋放鎖定。實(shí)現(xiàn)的代碼如下:
packagesyn3;
/**
*測(cè)試類
7
publicclassTestHuman{
publicstaticvoidmain(String[]args){
Toilett=newToilet();〃衛(wèi)生間對(duì)象
Humanhl=newHuman("l",t);
Humanh2=newHuman("2",t);
Humanh3=newHuman("3",t);
}
)
packagesyn3;
/**
*人線程類,演示互斥
*/
publicclassHumanextendsThread{
Toilett;
Stringname;
publicHuman(Stringnamejoilett){
=name;
this.t=t;
start。;〃啟動(dòng)線程
)
publicvoidrun(){
〃進(jìn)入衛(wèi)生間
t.enter(name);
)
}
packagesyn3;
/**
*衛(wèi)生間,互斥的演示
*/
publicclassToilet{
publicsynchronizedvoidenter(Stringname){
System.out.println(name+"已進(jìn)入!");
try(
Thread.sleep(2000);
}catch(Exceptione){}
System.out.println(name+"離開(kāi)!");
)
)
該示例的執(zhí)行結(jié)果為,不同次數(shù)下執(zhí)行結(jié)果會(huì)有所不同:
1已進(jìn)入!
1離開(kāi)!
3已進(jìn)入!
3離開(kāi)!
2已進(jìn)入!
2離開(kāi)!
在該示例代碼中,Toilet類表示衛(wèi)生間類,Human類模擬人,是該示
例中的線程類,TestHuman類是測(cè)試類,用于啟動(dòng)線程。在TestHuman中,
首先創(chuàng)建一個(gè)Toilet類型的對(duì)象t,并將該對(duì)象傳遞到后續(xù)創(chuàng)建的線程對(duì)象中,
這樣后續(xù)的線程對(duì)象就使用同一個(gè)Toilet對(duì)象,該對(duì)象就成為了臨界資源。
下面創(chuàng)建了三個(gè)Human類型的線程對(duì)象,每個(gè)線程具有自己的名稱name參
數(shù),模擬3個(gè)線程,在每個(gè)線程對(duì)象中,只是調(diào)用對(duì)象t中的enter方法,模
擬進(jìn)入衛(wèi)生間的動(dòng)作,在enter方法中,在進(jìn)入時(shí)輸出調(diào)用該方法的線程進(jìn)入,
然后延遲2秒,輸出該線程離開(kāi),然后后續(xù)的一個(gè)線程進(jìn)入,直到三個(gè)線程都
完成enter方法則程序結(jié)束。
在該示例中,同一個(gè)Toilet類的對(duì)象t的enter方法由于具有
synchronized修飾符修飾,則在多個(gè)線程同時(shí)調(diào)用該方法時(shí),如果一個(gè)線程進(jìn)
入到enter方法內(nèi)部,則為對(duì)象t上鎖,直到enter方法結(jié)束以后釋放對(duì)該對(duì)
象的鎖定,通過(guò)這種方式實(shí)現(xiàn)無(wú)論多少個(gè)Human類型的線程,對(duì)于同一個(gè)對(duì)
象3任何時(shí)候只能有一個(gè)線程執(zhí)行enter方法,這就是解決多線程問(wèn)題的第
一種思路——互斥的解決原理。
12.4.2同步
使用互斥解決多線程問(wèn)題是一種簡(jiǎn)單有效的解決辦法,但是由于該
方法比較簡(jiǎn)單,所以只能解決一些基本的問(wèn)題,對(duì)于復(fù)雜的問(wèn)題就無(wú)法解決
To
解決多線程問(wèn)題的另外一種思路是同步。同步是另外一種解決問(wèn)題
的思路,結(jié)合前面衛(wèi)生間的示例,互斥方式解決多線程的原理是,當(dāng)一個(gè)人
進(jìn)入到衛(wèi)生間內(nèi)部時(shí),別的人只能在外部時(shí)刻等待,這樣就相當(dāng)于別的人雖
然沒(méi)有事情做,但是還是要占用別的人的時(shí)間,浪費(fèi)系統(tǒng)的執(zhí)行資源。而同
步解決問(wèn)題的原理是,如果一個(gè)人進(jìn)入到衛(wèi)生間內(nèi)部時(shí),則別的人可以去睡
覺(jué),不占用系統(tǒng)資源,而當(dāng)這個(gè)人從衛(wèi)生間出來(lái)以后,把這個(gè)睡覺(jué)的人叫醒,
則它就可以使用臨界資源了。所以使用同步的思路解決多線程問(wèn)題更加有
效,更加節(jié)約系統(tǒng)的資源。
在常見(jiàn)的多線程問(wèn)題解決中,同步問(wèn)題的典型示例是“生產(chǎn)者-消費(fèi)
者”模型,也就是生產(chǎn)者線程只負(fù)責(zé)生產(chǎn),消費(fèi)者線程只負(fù)責(zé)消費(fèi),在消費(fèi)
者發(fā)現(xiàn)無(wú)內(nèi)容可消費(fèi)時(shí)則睡覺(jué)。下面舉一個(gè)比較實(shí)際的例子一一生活費(fèi)問(wèn)題。
生活費(fèi)問(wèn)題是這樣的:學(xué)生每月都需要生活費(fèi),家長(zhǎng)一次預(yù)存一段
時(shí)間的生活費(fèi),家長(zhǎng)和學(xué)生使用統(tǒng)一的一個(gè)帳號(hào),在學(xué)生每次取帳號(hào)中一部
分錢,直到帳號(hào)中沒(méi)錢時(shí)通知家長(zhǎng)存錢,而家長(zhǎng)看到帳戶還有錢則不存錢,
直到帳戶沒(méi)錢時(shí)才存錢。在這個(gè)例子中,這個(gè)帳號(hào)被學(xué)生和家長(zhǎng)兩個(gè)線程同
時(shí)訪問(wèn),則帳號(hào)就是臨界資源,兩個(gè)線程是同時(shí)執(zhí)行的,當(dāng)每個(gè)線程發(fā)現(xiàn)不
符合要求時(shí)則等待,并釋放分配給自己的CPU執(zhí)行時(shí)間,也就是不占用系統(tǒng)
資源。實(shí)現(xiàn)該示例的代碼為:
packagesyn4;
/**
*測(cè)試類
*/
publicclassTestAccount{
publicstaticvoidmain(String[]args){
Accouta=newAccout();
StudentThreads=newStudentThread(a);
GenearchThreadg=newGenearchThread(a);
)
)
packagesyn4;
*模擬學(xué)生線程
*/
publicclassStudentThreadextendsThread{
Accouta;
publicStudentThread(Accouta){
this.a=a;
start();
)
publicvoidrun(){
try(
while(true){
Thread.sleep(2000);
a.getMoney();〃取錢
)
}catch(Exceptione){}
)
)
packagesyn4;
*家長(zhǎng)線程
*/
publicclassGenearchThreadextendsThread{
Accouta;
publicGenearchThread(Accouta){
this.a=a;
start();
)
publicvoidrun(){
try(
while(true){
Thread.sleep(12000);
a.saveMoney。;〃存錢
}
}catch(Exceptione){}
)
}
packagesyn4;
/**
*銀行賬戶
*/
publicclassAccout{
intmoney=0;
*
*取錢
*如果賬戶沒(méi)錢則等待,否則取出所有錢提醒存錢
*/
publicsynchronizedvoidgetMoney(){
System.out.println("準(zhǔn)備取錢!");
try(
iffmoney==0){
wait。;〃等待
)
〃取所有錢
System.out.printin("乘lj余:"+money);
money-=50;
〃提醒存錢
notify();
}catch(Exceptione){}
}
/**
*存錢
*如果有錢則等待,否則存入200提醒取錢
*/
publicsynchronizedvoidsaveMoney(){
System.out.println("準(zhǔn)備存錢!");
try(
if(money!=0){
wait();〃等待
)
〃取所有錢
money=200;
System.out.printin("存入:"+money);
〃提醒存錢
notify();
}catch(Exceptione){}
)
)
該程序的一部分執(zhí)行結(jié)果為:
準(zhǔn)備取錢!
準(zhǔn)備存錢!
存入:200
剩余:200
準(zhǔn)備取錢!
剩余:150
準(zhǔn)備取錢!
剩余:100
準(zhǔn)備取錢!
剩余:50
準(zhǔn)備取錢!
準(zhǔn)備存錢!
存入:200
剩余:200
準(zhǔn)備取錢!
剩余:150
準(zhǔn)備取錢!
剩余:100
準(zhǔn)備取錢!
剩余:50
準(zhǔn)備取錢!
在該示例代碼中,TestAccount類是測(cè)試類,主要實(shí)現(xiàn)創(chuàng)建帳戶
Account類的對(duì)象,以及啟動(dòng)學(xué)生線程StudentThread和啟動(dòng)家長(zhǎng)線程
GenearchThreado在StudentThread線程中,執(zhí)行的功能是每隔2秒中取一次
錢,每次取50元。在GenearchThread線程中,執(zhí)行的功能是每隔12秒存一次
錢,每次存200。這樣存款和取款之間不僅時(shí)間間隔存在差異,而且數(shù)量上也
會(huì)出現(xiàn)交叉。而該示例中,最核心的代碼是Account類的實(shí)現(xiàn)。
在Account類中,實(shí)現(xiàn)了同步控制功能,在該類中包含一個(gè)關(guān)鍵的
屬性money,該屬性的作用是存儲(chǔ)帳戶金額。在介紹該類的實(shí)現(xiàn)前,首先介
紹一下兩個(gè)同步方法——wait和notify方法的使用,這兩個(gè)方法都是Object
類中的方法,也就是說(shuō)每個(gè)類都包含這兩個(gè)方法,換句話說(shuō),就是Java天生
就支持同步處理。這兩個(gè)方法都只能在synchronized修飾的方法或語(yǔ)句塊內(nèi)
部采用被調(diào)用。其中wait方法的作用是使調(diào)用該方法的線程休眠,也就是使
該線程退出CPU的等待隊(duì)列,處于冬眠狀態(tài),不執(zhí)行動(dòng)作,也不占用CPU排
隊(duì)的時(shí)間,notify方法的作用是喚醒一個(gè)任意該對(duì)象的線程,該線程當(dāng)前處于
休眠狀態(tài),至于喚醒的具體是那個(gè)則不保證。在Account類中,被StudentThread
調(diào)用的getMoney方法的功能是判斷當(dāng)前金額是否是0,如果是則使
StudentThread線程處于休眠狀態(tài),如果金額不是0,則取出50元,同時(shí)喚醒
使用該帳戶對(duì)象的其它一個(gè)線程,而被GenearchThread線程調(diào)用的
saveMoney方法的功能是判斷當(dāng)前是否不為0,如果是則使GenearchThread
線程處于休眠狀態(tài),如果金額是0,則存入200元,同時(shí)喚醒使用該帳戶對(duì)象
的其它一個(gè)線程。
如果還是不清楚,那就結(jié)合前面的程序執(zhí)行結(jié)果來(lái)解釋一下程序執(zhí)
行的過(guò)程:在程序開(kāi)始執(zhí)行時(shí),學(xué)生線程和家長(zhǎng)線程都啟動(dòng)起來(lái),所以輸出
“準(zhǔn)備取錢”和“準(zhǔn)備存錢”,然后學(xué)生線程按照該線程run方法的邏輯執(zhí)
行,先延遲2秒,然后調(diào)用帳戶對(duì)象a中的getMoney方法,但是由于初始情
況下帳戶對(duì)象a中的money數(shù)值為0,所以學(xué)生線程就休眠了。在學(xué)生線程
執(zhí)行的同時(shí),家長(zhǎng)線程也按照該線程的run方法的邏輯執(zhí)行,先延遲12秒,
然后調(diào)用帳戶對(duì)象a中的saveMoney方法,由于帳戶a對(duì)象中的money為零,
條件不成立,所以執(zhí)行存入200元,同時(shí)喚醒線程,由于使用對(duì)象a的線程現(xiàn)
在只有學(xué)生線程,所以學(xué)生線程被喚醒,開(kāi)始執(zhí)行邏輯,取出50元,然后喚
醒線程,由于當(dāng)前沒(méi)有線程處于休眠狀態(tài),所以沒(méi)有線程被喚醒。同時(shí)家長(zhǎng)
線程繼續(xù)執(zhí)行,先延遲12秒,這個(gè)時(shí)候?qū)W生線程執(zhí)行了4次,耗時(shí)4X2秒=8秒,
就取光了帳戶中的錢,接著由于帳戶為0則學(xué)生線程又休眠了,一直到家長(zhǎng)線
程延遲12秒結(jié)束以后,判斷帳戶為0,又存入了200元,程序繼續(xù)執(zhí)行下去。
在解決多線程問(wèn)題是,互斥和同步都是解決問(wèn)題的思路,如果需要
形象的比較這兩種方式的區(qū)別的話,就看一下下面的示例。一個(gè)比較忙的老
總,桌子上有2部電話,在一部處于通話狀態(tài)時(shí),另一部響了,老總拿其這部
電話說(shuō)我在接電話,你等一下,而沒(méi)有掛電話,這種處理的方式就是互斥。
而如果老總拿其另一部電話說(shuō),我在接電話,等會(huì)我打給你,然后掛了電話,
這種處理的方式就是同步。兩者相比,互斥明顯占用系統(tǒng)資源(浪費(fèi)電話費(fèi),
浪費(fèi)別人的時(shí)間),而同步則是一種更加好的解決問(wèn)題的思路。
12.4.3死鎖
多線程編程在實(shí)際的網(wǎng)絡(luò)程序開(kāi)發(fā)中,在客戶端程序?qū)崿F(xiàn)中使用的
比較簡(jiǎn)單,但是在服務(wù)器端程序?qū)崿F(xiàn)中卻不僅是大量使用,而且會(huì)出現(xiàn)比客
戶端更多的問(wèn)題。
另外一個(gè)容易在服務(wù)器端出現(xiàn)的多線程問(wèn)題是——死鎖。死鎖指兩
個(gè)或兩個(gè)以上的線程為了使用某個(gè)臨界資源而無(wú)限制的等待下去。還是以前
面衛(wèi)生間的例子來(lái)說(shuō)明死鎖,例如兩個(gè)人都同時(shí)到達(dá)衛(wèi)生間,而且兩個(gè)人都
比較禮貌,第一個(gè)人和第二個(gè)人說(shuō):你先吧,第二個(gè)人和第一個(gè)人說(shuō):你先
吧。這兩個(gè)人就這樣一直在互相禮讓,誰(shuí)也不進(jìn)入,這種現(xiàn)象就是死鎖。這
里的兩個(gè)人就好比是線程,而衛(wèi)生間在這里就是臨界資源,而由于這兩個(gè)線
程在一直謙讓,誰(shuí)也不使用臨界資源。
死鎖不僅使程序無(wú)法達(dá)到預(yù)期實(shí)現(xiàn)的功能,而且浪費(fèi)系統(tǒng)的資源,
所以在服務(wù)器端程序中危害比較大,在實(shí)際的服務(wù)器端程序開(kāi)發(fā)中,需要注
意避免死鎖。
而死鎖的檢測(cè)比較麻煩,而且不一定每次都出現(xiàn),這就需要在測(cè)試
服務(wù)器端程序時(shí),有足夠的耐心,仔細(xì)觀察程序執(zhí)行時(shí)的性能檢測(cè),如果發(fā)
現(xiàn)執(zhí)行的性能顯著降低,則很可能是發(fā)生了死鎖,然后再具體的查找死鎖出
現(xiàn)的原因,并解決死鎖的問(wèn)題。
死鎖出現(xiàn)的最本質(zhì)原因還是邏輯處理不夠嚴(yán)謹(jǐn),在考慮時(shí)不是很周
全,所以一般需要修改程序邏輯才能夠很好的解決死鎖。
12.4.4線程優(yōu)先級(jí)
在日常生活中,例如火車售票窗口等經(jīng)??梢钥吹健癤XX優(yōu)先”,
那么多線程編程中每個(gè)線程是否也可以設(shè)置優(yōu)先級(jí)呢?
在多線程編程中,支持為每個(gè)線程設(shè)置優(yōu)先級(jí)。優(yōu)先級(jí)高的線程在
排隊(duì)執(zhí)行時(shí)會(huì)獲得更多的CPU執(zhí)行時(shí)間,得到更快的響應(yīng)。在實(shí)際程序中,
可以根據(jù)邏輯的需要,將需要得到及時(shí)處理的線程設(shè)置成較高的優(yōu)先級(jí),而
把對(duì)時(shí)間要求不高的線程設(shè)置成比較低的優(yōu)先級(jí)。
在Thread類中,總計(jì)規(guī)定了三個(gè)優(yōu)先級(jí),分別為:
?MAX_PRIORITY------最高優(yōu)先級(jí)
?NORM_PRIORITY——普通優(yōu)先級(jí),也是默認(rèn)優(yōu)先級(jí)
?MINPRIORITY-?最低優(yōu)先級(jí)
在前面創(chuàng)建的線程對(duì)象中,由于沒(méi)有設(shè)置線程的優(yōu)先級(jí),則線程默認(rèn)的優(yōu)
先級(jí)是NORM_PRIORITY,在實(shí)際使用時(shí),也可以根據(jù)需要使用Thread類中的
setPriority方法設(shè)置線程的優(yōu)先級(jí),該方法的聲明為:
publicfinalvoidsetPriority(intnewPriority)
假設(shè)t是一個(gè)初始化過(guò)的線程對(duì)象,需要設(shè)置t的優(yōu)先級(jí)為最高,則實(shí)現(xiàn)
的代碼為:
t.setPriority(Thread.MAX_PRIORITY);
這樣,在該線程執(zhí)行時(shí)將獲得更多的執(zhí)行機(jī)會(huì),也就是優(yōu)先執(zhí)行。如果由
于安全等原因,不允許設(shè)置線程的優(yōu)先級(jí),則會(huì)拋出SecurityException異常。
下面使用一個(gè)簡(jiǎn)單的輸出數(shù)字的線程演示線程優(yōu)先級(jí)的使用,實(shí)現(xiàn)的示例
代碼如下:
packagepriority;
/**
*測(cè)試線程優(yōu)先級(jí)
*/
publicclassTestPriority{
publicstaticvoidmain(String[]args){
PrintNumberThreadpl=new
PrintNumberThread("高優(yōu)先級(jí))
PrintNumberThreadp2=new
PrintNumberThread("普通優(yōu)先級(jí)”);
PrintNumberThreadp3=new
PrintNumberThread("低優(yōu)先級(jí)”);
pl.setPriority(Thread.MAX_PRIORITY);
p2.setPriority(Thread.NORM_PRIORITY);
p3.setPriority(Thread.MIN_PRIORITY);
pl.start();
p2.start();
p3.start();
)
}
packagepriority;
/**
*輸出數(shù)字的線程
*/
publicclassPrintNumberThreadextendsThread{
Stringname;
publicPrintNumberThread(Stringname){
=name;
)
publicvoidrun(){
try(
for(inti=0;i<10;i++){
System.out.printin(name+
)
}catch(Exceptione){}
)
)
程序的一種執(zhí)行結(jié)果為:
高優(yōu)先級(jí):o
高優(yōu)先級(jí):1
高優(yōu)先級(jí):2
普通優(yōu)先級(jí):0
高優(yōu)先級(jí):3
普通優(yōu)先級(jí):1
高優(yōu)先級(jí):4
普通優(yōu)先級(jí):2
高優(yōu)先級(jí):5
高優(yōu)先級(jí):6
高優(yōu)先級(jí):7
高優(yōu)先級(jí):8
高優(yōu)先級(jí):9
普通優(yōu)先級(jí):3
普通優(yōu)先級(jí):4
普通優(yōu)先級(jí):5
普通優(yōu)先級(jí):6
普通優(yōu)先級(jí):7
普通優(yōu)先級(jí):
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 停車位建設(shè)項(xiàng)目可行性報(bào)告
- 大學(xué)生讀書心得筆記
- 租房合同范本集錦15篇
- 啟動(dòng)儀式領(lǐng)導(dǎo)講話稿(集合15篇)
- 手機(jī)銷售辭職報(bào)告15篇
- 關(guān)于小學(xué)個(gè)人教師述職報(bào)告十篇
- 數(shù)學(xué)教學(xué)心得體會(huì)
- 房地產(chǎn)銷售個(gè)人工作總結(jié)(匯編15篇)
- 幼兒園班主任辭職報(bào)告錦集7篇
- 新媒體營(yíng)銷(第三版) 課件 項(xiàng)目二 新媒體營(yíng)銷定位與策劃
- 網(wǎng)絡(luò)運(yùn)維從入門到精通29個(gè)實(shí)踐項(xiàng)目詳解
- 2024屆黃岡市啟黃中學(xué)中考試題猜想數(shù)學(xué)試卷含解析
- 摩擦阻力系數(shù)公式計(jì)算
- 旱稻栽培管理技術(shù)
- 自費(fèi)藥品知情同意書
- (完整版)泌尿外科手術(shù)分級(jí)目錄
- 2023-2024學(xué)年鄧州市數(shù)學(xué)四年級(jí)第一學(xué)期期末聯(lián)考試題含答案
- 2021年新疆烏魯木齊市中考化學(xué)一模試卷(附答案詳解)
- 張家爺爺?shù)男』ü?
- 高中思想政治-高三一輪復(fù)習(xí)講評(píng)課教學(xué)課件設(shè)計(jì)
- 自動(dòng)噴水滅火系統(tǒng)的設(shè)計(jì)計(jì)算
評(píng)論
0/150
提交評(píng)論