Java Threads 3r. Ed.
Java Threads 3r. Ed.
Supplementalfilesandexamplesforthisbookcanbefoundathttp://examples.oreilly.com/9780596007829/(http://examples.oreilly.com/9780596007829/).Please
useastandarddesktopwebbrowsertoaccessthesefiles,astheymaynotbeaccessiblefromallereaderdevices.
Allcodefilesorexamplesreferencedinthebookwillbeavailableonline.Forphysicalbooksthatshipwithanaccompanyingdisc,wheneverpossible,weve
postedallCD/DVDcontent.Notethatwhileweprovideasmuchofthemediacontentasweareableviafreedownload,wearesometimeslimitedby
licensingrestrictions.Pleasedirectanyquestionsorconcernstobooktech@oreilly.com.
Preface
WhenSunMicrosystemsreleasedthealphaversionofJava inthewinterof1995,developersallovertheworldtooknotice.Thereweremanyfeaturesof
Javathatattractedthesedevelopers,nottheleastofwhichwerethesetofbuzzwordsSunusedtopromotethelanguage.Javawas,amongotherthings,
robust,safe,architectureneutral,portable,objectoriented,simple,andmultithreaded.Formanydevelopers,theselasttwobuzzwordsseemedcontradictory:
howcouldalanguagethatismultithreadedbesimple?
ItturnsoutthatJava'sthreadingsystemissimple,atleastrelativetootherthreadingsystems.ThissimplicitymakesJava'sthreadingsystemeasytolearnso
thatevendeveloperswhoareunfamiliarwiththreadscanpickupthebasicsofthreadprogrammingwithrelativeease.
InearlyversionsofJava,thissimplicitycamewithtradeoffssomeoftheadvancedfeaturesthatarefoundinotherthreadingsystemswerenotavailablein
Java.Java2StandardEditionVersion5.0(J2SE5.0)changesallofthatitprovidesalargenumberofnewthreadrelatedclassesthatmakethetaskofwriting
multithreadedprogramsthatmucheasier.
Still,programmingwiththreadsremainsacomplextask.ThisbookshowsyouhowtousethethreadingtoolsinJavatoperformthebasictasksofthreaded
programmingandhowtoextendthemtoperformmoreadvancedtasksformorecomplexprograms.
ThesenewAPIsinclude:
Atomicvariables
Asetofclassesthatprovidethreadsafeoperationswithoutsynchronization
Explicitlocks
Synchronizationlocksthatcanbeacquiredandreleasedprogrammatically
Conditionvariables
Variablesthatcanbethesubjectofatargetednotificationwhencertainconditionsexist
Queues
Collectionclassesthatarethreadaware
Synchronizationprimitives
Newclassesthatperformcomplextypesofsynchronization
Threadpools
Classesthatcanmanageapoolofthreadstoruncertaintasks
Threadschedulers
Classesthatcanexecutetasksataparticularpointintime
We'vefullyintegratedthenewfeaturesofJ2SE5.0throughoutthetextofthisedition.Thenewfeaturescanbesplitintothreecategories:
Newimplementationsofexistingfeatures
TheJavalanguagehasalwayshadthecapabilitytoperformdatasynchronizationandthreadnotification.However,implementationofthesefeatureswas
somewhatlimitedyoucould,forexample,synchronizeblocksofcodeorentiremethodsbutsynchronizingacrossmethodsandclassesrequiredextra
programming.InJ2SE5.0,explicitlocksandconditionvariablesallowyoumoreflexibilitywhenusingthesefeatures.
Thesenewimplementationsdonotintroducenewconceptsforadeveloper.Adeveloperwhowantstowriteathreadsafeprogrammustensurethather
dataiscorrectlysynchronized,whethersheusesJ2SE5.0'sexplicitlocksorthemorebasicsynchronizedkeyword.Therefore,botharepresented
togetherwhenwetalkaboutdatasynchronization.Thesameistrueofconditionvariables,whichprovidethreadnotificationandarediscussedalongside
Java'swait()andnotify()methods,andofqueues,whicharediscussedalongwithJava'sothercollectionclasses.
Importantthreadutilities
Atsomepointintime,virtuallyalldeveloperswhowritethreadedprogramswillneedtousebasicthreadutilitiessuchasapooloraschedulermanyof
themwillalsoneedtouseadvancedsynchronizationprimitives.ArecognitionofthisfactisonethingthatdroveJSR166itwascertainlypossiblein
previousversionsofJavatodevelopyourownthreadpoolsandschedulers.ButgiventheimportanceofthreadingintheJavaplatform,addingthese
basicutilitiesgreatlyincreasesprogrammerproductivity.
Minimalsynchronizationutilities
Java'snewatomicclassesprovideameansbywhichdeveloperscan,whennecessary,writeapplicationsthatavoidsynchronization.Thiscanleadto
programsthatarehighlyconcurrent.
Ifyou'vereadpreviouseditionsofthisbook,theconceptspresentedinthefirsttwocategorieswillbefamiliar.Inpreviouseditions,wedevelopedourown
datasynchronizationclasses,threadpools,andsoon.Inthoseeditions,weexplainedindetailhowourimplementationsworkedandthenusedthemin
severalexamples.Inthisedition,wefocussolelyonhowtousetheseclasseseffectively.
Theinformationthatfallsintothethirdcategoryiscompletelynewtothisedition.Theclassesthatperformminimalsynchronizationrequirenewsupport
fromthevirtualmachineitselfandcouldnotbedevelopedindependentofthosechanges.
Chapter2
Thischaptershowsyouhowtocreatethreadsandrunnableobjectswhileexplainingthebasicprinciplesofhowthreadswork.
Chapter3
Thischapterdiscussesthebasiclevelatwhichthreadssharedatasafelycoordinatingwhichthreadisallowedtoaccessdataatanytime.Sharingdata
betweenthreadsistheunderlyingtopicofournextfourchapters.
Chapter4
Thischapterdiscussesthebasictechniquethreadsusetocommunicatewitheachotherwhentheyhavechangeddata.Thisallowsthreadstorespondto
datachangesinsteadofpollingforsuchchanges.
Chapter5
Thischapterdiscussesclassesandprogrammingmethodsthatachievedatasafetywhileusingaminimalamountofsynchronization.
Chapter6
Inthischapter,wecompleteourexaminationofdatasharingandsynchronizationwithanexaminationofdeadlock,starvation,andmiscellaneouslocking
classes.
Chapter7
Swingclassesarenotthreadsafe.ThischapterdiscusseshowmultithreadedprogramscantakefulladvantageofSwing.
Chapter8
Javacollectionclassesarewrittenforavarietyofcircumstances.Somearethreadsafeandsomearenot,andJ2SE5.0introducesnewcollectionclasses
forusespecificallywiththreadutilities.Wesortallthatoutinthischapter.
Chapter9
SchedulingistheprocesswherebyasingleCPUselectsathreadtorun.Threadschedulingismoreapropertyofanoperatingsystem(OS)thanaJava
program,andthischapterdiscussestherelationshipbetweenthevirtualmachineandtheOSinthisarea.
Chapter10
Thischapterdiscussesthreadpoolsacollectionofthreadsthatcanbeusedtorunarbitrarytasks.WeusethethreadpoolimplementationofJ2SE5.0
fordiscussionofthegeneralprinciplesofusingthreadpools.
Chapter11
Taskschedulersexecuteataskoneormoretimesatsomepointinthefuture.Thissetofclassesincludestimers(JavahashadtimerclassessinceJDK
1.3)andageneraltaskscheduleravailableinJ2SE5.0.
Chapter12
DealingwithI/OisoneoftheprimaryreasonswhydevelopersusethreadsinJava.Inthischapter,weuseallofJava'sthreadingfeaturestoshowyou
howtohandleI/Oeffectivelyinmultithreadedprograms.
Chapter13
Inthischapter,wecompleteourexaminationofthreadrelatedfeaturesofJavabyexaminingthreadsecurity,threadgroups,threadstacks,andother
topics.
Chapter14
Performanceofthreadrelatedfeaturesandparticularlysynchronizationconstructsiskeytowritingmultithreadedprograms.Inthischapter,wetest
variouslowlevelprogrammingfeaturesandexploresometruthsandmythsaboutthreadperformance.
Chapter15
Inthischapter,weshowaprocessforexploitingthepowerofmultiprocessormachinestocalculateCPUintensiveloopsinparallel.
AppendixA
J2SE5.0introducesanumberofthreadrelatedclasses.Manyoftheseclassesaresimilartoclassesdevelopedinpreviouseditionsofthisbookwelist
thoseclassesinthisappendixasanaidtodeveloperswhocannotyetupgradetoJ2SE5.0.
Indicatescodeexamples,methods,variables,parameters,andkeywordswithinthetext.
Constantwidthbold
Indicatesuserinput,suchascommandsthatyoutypeonthecommandline.
Code Examples
Allexamplespresentedinthebookarecomplete,runningapplications.However,manyoftheprogramlistingsareshortenedbecauseofspaceandreadability
considerations.Thefullexamplesmayberetrievedonlinefromhttp://www.oreilly.com/catalog/jthreads3(http://www.oreilly.com/catalog/jthreads3).
Thisbookisheretohelpyougetyourjobdone.Ingeneral,youmayusethecodeinthisbookinyourprogramsanddocumentation.Youdonotneedto
contactusforpermissionunlessyou'rereproducingasignificantportionofthecode.Forexample,writingaprogramthatusesseveralchunksofcodefrom
thisbookdoesnotrequirepermission.SellingordistributingaCDROMofexamplesfromO'Reillybooksdoesrequirepermission.Answeringaquestion
bycitingthisbookandquotingexamplecodedoesnotrequirepermission.Incorporatingasignificantamountofexamplecodefromthisbookintoyour
product'sdocumentationdoesrequirepermission.
Weappreciate,butdonotrequire,attribution.Anattributionusuallyincludesthetitle,author,publisher,andISBN.Forexample:"JavaThreads,Third
Edition,byScottOaksandHenryWong.Copyright2004O'ReillyMedia,0596007825."
Ifyoufeelyouruseofcodeexamplesfallsoutsidefairuseorthepermissiongivenabove,feelfreetocontactusatpermissions@oreilly.com.
How to Contact Us
Pleaseaddresscommentsandquestionsconcerningthisbooktothepublisher:
O'ReillyMedia,Inc.
1005GravensteinHighwayNorth
Sebastopol,CA95472
(800)9989938(intheUnitedStatesorCanada)
(707)8290515(internationalorlocal)
(707)8290104(fax)
O'Reillymaintainsawebpageforthisbook,wherewelisterrata,examples,andanyadditionalinformation.Youcanaccessthispageat:
http://www.oreilly.com/catalog/jthreads3(http://www.oreilly.com/catalog/jthreads3)
Tocommentorasktechnicalquestionsaboutthisbook,sendemailto:
bookquestions@oreilly.com
FormoreinformationaboutO'Reillybooks,conferences,ResourceCenters,andtheO'ReillyNetwork,seeourwebsiteat:
http://www.oreilly.com(http://www.oreilly.com)
Safari Enabled
WhenyouseetheSafariEnabledicononthebackcoverofyourfavoritetechnologybook,thatmeansthebookisavailableonlinethroughtheO'Reilly
NetworkSafariBookshelf.
Safarioffersasolutionthat'sbetterthanebooks.It'savirtuallibrarythatletsyoueasilysearchthousandsoftoptechnologybooks,cutandpastecode
samples,downloadchapters,andfindquickanswerswhenyouneedthemostaccurate,currentinformation.
Tryitforfreeathttp://safari.oreilly.com(http://safari.oreilly.com).
Acknowledgments
Asreadersofprefacesarewellaware,writingabookisneveraneffortundertakensolelybytheauthorswhogetallthecreditonthecover.Wearedeeply
indebtedtothefollowingpeoplefortheirhelpandencouragement:MichaelLoukides,whobelieveduswhenwesaidthatthiswasanimportanttopicand
whoshepherdedusthroughthecreativeprocessDavidFlanagan,forvaluablefeedbackonthedraftsDebCameron,foreditingsometimesramblingtextinto
coherencyHongZhang,forhelpinguswithWindowsthreadingissuesandReynoldJabbour,WendyTalmont,SteveWilson,andTimCramerfor
supportingusinourworkoverthepastsixyears.
Mostly,wemustthankourrespectivefamilies.ToJames,whogaveScottthesupportandencouragementnecessarytoseethisbookthrough(andtocope
withhiscontinualstateofdistraction),andtoNini,whoknewtoleaveHenryaloneforthetenpercentofthetimewhenhewascreative,andencouragedhim
therestofthetimethankyouforeverything!
Finally,wemustthankthemanyreadersoftheearliereditionsofthisbookwhosentusinvaluablefeedback.Wehavetriedourbesttoanswereveryconcern
thattheyhaveraised.Keepthosecardsandletterscoming!
Chapter1.Introduction to Threads
ThisisabookaboutusingthreadsintheJavaprogramminglanguageandtheJavavirtualmachine.ThetopicofthreadsisveryimportantinJavaso
importantthatmanyfeaturesofthethreadingsystemarebuiltintotheJavalanguageitselfwhileotherfeaturesofthethreadingsystemarerequiredbythe
Javavirtualmachine.ThreadingisanintegralpartofusingJava.
Theconceptofthreadsisnotanewone:forsometime,manyoperatingsystemshavehadlibrariesthatprovidetheCprogrammeramechanismtocreate
threads.Otherlanguages,suchasAda,havesupportforthreadsembeddedintothelanguage,muchassupportforthreadsisbuiltintotheJavalanguage.
Nonetheless,untilJavacamealong,thetopicofthreadswasusuallyconsideredaperipheralprogrammingtopic,onethatwasonlyneededinspecial
programmingcases.
WithJava,thingsaredifferent:itisimpossibletowriteanybutthesimplestJavaprogramwithoutintroducingthetopicofthreads.AndthepopularityofJava
ensuresthatmanydevelopers,whomightneverhaveconsideredlearningaboutthreadingpossibilitiesinalanguagesuchasCorC++,needtobecomefluent
inthreadedprogramming.
Futhermore,theJavaplatformhasmaturedthroughouttheyears.InJava2StandardEditionVersion5.0(J2SE5.0),theclassesavailableforthreadrelated
programmingrivalmanyprofessionalthreadingpackages,mitigatingtheneedtouseanycommerciallibrary(aswassomewhatcommoninpreviousreleases
ofJava).SoJavadevelopersnotonlyneedtobecomeknowledgeableinthreadedprogrammingtowritebasicapplicationsbutwillwanttolearnthecomplete,
richsetofclassesavailableforwritingcomplex,commercialgradeapplications.
Java Terms
Let'sstartbydefiningsometermsusedthroughoutthisbook.ManyJavarelatedtermsareusedinconsistentlyinvarioussourcesweendeavortobe
consistentinourusageofthesetermsthroughoutthebook.
Java
First,isthetermJavaitself.Asyouknow,Javastartedoutasaprogramminglanguage,andmanypeopletodaystillthinkofJavaasbeingsimplya
programminglanguage.ButJavaismuchmorethanjustaprogramminglanguage:it'salsoanAPIspecificationandavirtualmachinespecification.So
whenwesayJava,wemeantheentireJavaplatform:theprogramminglanguage,itsAPIs,andavirtualmachinespecificationthat,takentogether,define
anentireprogrammingandruntimeenvironment.OftenwhenwesayJava,it'sclearfromthecontextthatwe'retalkingspecificallyaboutthe
programminglanguage,orpartsoftheJavaAPI,orthevirtualmachine.Thepointtorememberisthatthethreadingfeatureswediscussinthisbook
derivetheirpropertiesfromallthecomponentsoftheJavaplatformtakenasawhole.Whileit'spossibletotaketheJavaprogramminglanguage,directly
compileitintoassemblycode,andrunitoutsideofthevirtualmachine,suchanexecutablemaynotnecessarilybehavethesameastheprogramswe
describeinthisbook.
Virtualmachine,interpreters,andbrowsers
TheJavavirtualmachineisthecodethatactuallyrunsaJavaprogram.ItspurposeistointerprettheintermediatebytecodesthatJavaprogramsare
compiledintothevirtualmachineissometimescalledtheJavainterpreter.However,modernvirtualmachinesusuallycompilethemajorityofthecode
theyrunintonativeinstructionsastheprogramisexecutingtheresultisthatthevirtualmachinedoeslittleactualinterpretationofcode.
BrowserssuchasMozilla,NetscapeNavigator,Opera,andInternetExplorerallhavethecapabilitytoruncertainJavaprograms(applets).Historically,
thesebrowsershadanembeddedvirtualmachinetoday,thestandardJavavirtualmachinerunsasaplugintothesebrowsers.Thatmeansthatthe
threadingdetailsofJavacapablebrowsersareessentiallyidenticaltothoseofastandardJavavirtualmachine.Theonesignificantareaofdifferenceliesin
someofthedefaultthreadsecuritysettingsforbrowsers(seeChapter13).
Virtualmachineimplementationsareavailablefrommanydifferentvendorsandformanydifferentoperatingsystems.Forthemostpart,virtual
machinesareindistinguishableatleastintheory.However,becausethreadsaretiedtotheoperatingsystemonwhichtheyrun,platformspecific
differencesinthreadbehaviordocropup.Thesedifferencesareimportantinrelativelyfewcircumstances,andwediscusstheminChapter9.
Programs,applications,applets,andothercode
ThisleadsustothetermsthatweuseforthingswrittenintheJavalanguage.Liketraditionalprogrammingmodels,Javasupportstheideaofa
standaloneapplication,whichinthecaseofJavaisrunfromthecommandline(orthroughadesktopchooseroricon).ThepopularityofJavahasledto
thecreationofmanynewtypesofJavaenabledcontainersthatrunpiecesofJavacodecalledcomponents.Webservercontainersallowyoutowrite
components(servletsandJavaServerPageorJSPclasses)thatruninsidethewebserver.Javaenabledbrowsersallowyoutowriteapplets:classesthat
runinsidetheJavaplugin.Java2EnterpriseEdition(J2EE)applicationserversexecuteEnterpriseJavaBeans(EJBs),servlets,JSPs,andsoon.Even
databasesnowprovidetheabilitytouseserversideJavacomponents.
AsfarasJavathreadsareconcerned,thedistinctionbetweenthedifferenttypesofcontainersisusuallyonlythelocationoftheobjectstobeexecuted.
Certaincontainersplacerestrictionsonthreadedoperations(whichwediscussinChapter13),andinthatcase,wediscussspecificcomponents.Apart
fromtherarecasewherewespecificallymentionatypeofcomponent,wejustusethetermprogramsincetheconceptsdiscussedapplytoalloftheJava
codeyoumightwrite.
Concurrencyandthreads
J2SE5.0includesapackageknownasthe"concurrencyutilities,"orJSR166.Concurrencyisabroadterm.Itincludestheabilitytoperformmultiple
tasksatthesametimewegenerallyrefertothatabilityasparallelism.Aswe'llseethroughoutthisbook,threadedprogrammingisaboutmorethan
parallelism:it'salsoaboutsimplerprogramdesignandcopingwithcertainimplementationfeaturesoftheJavaplatform.ThefeaturesofJava(including
thoseofJSR166)helpuswiththesetasksaswell.
Concurrencyalsoincludestheabilitytoaccessdataatthesametimeintwoormorethreads.Theseareissuesofdatasynchronization,whichistheterm
weusewhendiscussingthoseaspectsofconcurrency.
WefocusinthisbookonJ2SE5.0.
Thisversioncontainsawealthofnewthreadrelatedclassesandfeatures.Theseclassesgreatlysimplifymuchofthe
workindevelopingthreadedapplicationssincetheyprovidebasicimplementationsofcommonthreadingparadigms.
ThenewfeaturesofJ2SE5.0areintegratedthroughouttheJavaplatformwe'veintegratedthenewfeaturesthroughoutourdiscussionaswell.Whenwe
discussJ2SE5.0,weclearlyidentifythenewfeaturesassuch.Ifyou'reunabletousethosefeaturesbecauseyoucannotyetupgradetheversionofJava
you'reusing,you'llfindsimilarfunctionalitytoalmostallJ2SE5.0featuresintheclassesprovidedintheAppendixA,whichcontainsimplementationsof
commonthreadingutilitiesthatweredevelopedinpreviousversionsofthisbooktheseutilitiesuseanearlierversionofJava.
ThepackagenametellsusthatthisisthesecondexampleinChapter2.Followingtheellipses,weseethatthereisanewinstancevariable(stopButton)
andsomenewcodeaddedtotheinitComponents()method.
Forreferencepurposes,welisttheexamplesandtheirmainclassattheendofeachchapter.
Whilethesourceargumentisnotneededforagreatmanyofourexamples,wealwaysuseitforconsistency.
Runningtheexamplesrequiresusingtheentirepackagenameforthemainclass:
piccolo%javajavathreads.examples.ch02.example1.SwingTypeTester
Itisalwayspossibletoruneachexampleinthisfashion:firstcompileallthefilesintheexampledirectoryandthenrunthespecificclass.Thiscanleadtoa
lotoftyping.Tomakethiseasier,we'vealsosuppliedanAntbuildfilethatcanbeusedtocompileandrunallexamples.
ANT
Onitshomepage,http://ant.apache.org(http://ant.apache.org),theauthorsdescribeAntas"aJavabasedbuildtool.Intheory,itiskindoflikeMake,but
withoutMake'swrinkles."Becauseit'swritteninJava,itisportableitsdesignmakesitextensibleaswell.
TouseAnt,youmustdownloaditfromhttp://ant.apache.org/(http://ant.apache.org/).Unzipthedownloadedarchive,andaddtheantbinarydirectoryto
yourpath.
Youdon'tneedtoknowanythingabouthowantworksinordertouseitforourexamples,butifyou'replanningondoingseriousJavadevelopment,
learningaboutantiswellworththe(ratherminimal)effort.
Theantbuildfilewesupplyhasatargetforeachexamplethatyoucanrunthesetargetsarenamedbychapterandexamplenumber.Forinstance,torunthe
firstexamplefromChapter2,youcanexecutethiscommand:
piccolo%antch2ex1
Theanttargetforeachexampleisalsolistedattheendofeachchapter.Someexamplesrequireacommandlineargument.Whenusingant,these
argumentshaveadefaultvalue(specifiedinthebuild.xmlfile)andcanbeoverriddenonthecommandline.Forexample,tospecifythenumberofthreadsfor
aparticularexampleinChapter5,youcanruntheexamplelikethis:
piccolo%antDCalcThreadCount=5ch5ex4
Thepropertiesandtheirdefaultsarelistedattheendofthechapter,likethis:
<propertyname="CalcThreadCount"value="10"/>
Why Threads?
ThenotionofthreadingissoingrainedinJavathatit'salmostimpossibletowriteeventhesimplestprogramsinJavawithoutcreatingandusingthreads.And
manyoftheclassesintheJavaAPIarealreadythreaded,sooftenyouareusingmultiplethreadswithoutrealizingit.
Historically,threadingwasfirstexploitedtomakecertainprogramseasiertowrite:ifaprogramcanbesplitintoseparatetasks,it'softeneasiertoprogramthe
algorithmasseparatetasksorthreads.Programsthatfallintothiscategoryaretypicallyspecializedanddealwithmultipleindependenttasks.Therelative
rarenessofthesetypesofprogramsmakesthreadinginthiscategoryaspecializedskill.Often,theseprogramswerewrittenasseparateprocessesusing
operatingsystemdependentcommunicationtoolssuchassignalsandsharedmemoryspacestocommunicatebetweenprocesses.Thisapproachincreased
systemcomplexity.
Thepopularityofthreadingincreasedwhengraphicalinterfacesbecamethestandardfordesktopcomputersbecausethethreadingsystemallowedtheuserto
perceivebetterprogramperformance.Theintroductionofthreadsintotheseplatformsdidn'tmaketheprogramsanyfaster,butitcreatedanillusionoffaster
performancefortheuser,whonowhadadedicatedthreadtoserviceinputordisplayoutput.
Inthe1990s,threadedprogramsbegantoexploitthegrowingnumberofcomputerswithmultipleprocessors.ProgramsthatrequirealotofCPUprocessing
arenaturalcandidatesforthiscategorysinceacalculationthatrequiresonehouronasingleprocessormachinecould(atleasttheoretically)runinhalfanhour
onatwoprocessormachineor15minutesonafourprocessormachine.Allthatisrequiredisthattheprogrambewrittentousemultiplethreadstoperform
thecalculation.
Althoughcomputerswithmultipleprocessorshavebeenaroundforalongtime,we'renowseeingthesemachinesbecomecheapenoughtobeverywidely
available.Theadventoflessexpensivemachineswithmultipleprocessors,andofoperatingsystemsthatprovideprogrammerswiththreadlibrariesto
exploitthoseprocessors,hasmadethreadedprogrammingahottopicasdevelopersmovetoextracteverybenefitfromthesemachines.UntilJava,muchof
theinterestinthreadingcenteredonusingthreadstotakeadvantageofmultipleprocessorsonasinglemachine.
However,threadinginJavaoftenhasnothingatalltodowithmultiprocessormachinesandtheircapabilitiesinfact,thefirstJavavirtualmachineswere
unabletotakeadvantageofmultipleprocessorsonamachine.ModernJavavirtualmachinesnolongersufferfromthislimitation,andamultithreadedJava
programtakesadvantageofalltheCPUsavailableonitshostmachine.However,evenifyourJavaprogramisdestinedtoberunonamachinewithasingle
CPU,threadingisstillveryimportant.
OnereasonthatthreadingisimportantinJavaisthat,untilJDK1.4,JavahadnoconceptofasynchronousbehaviorforI/O.Thismeantthatmanyofthe
programmingtechniquesyou'vebecomeaccustomedtousingintypicalprogramswerenotapplicableinJavainstead,untilrecently,Javaprogrammershad
tousethreadingtechniquestohandleasynchronousbehavior.AnotherreasonisthegraphicalnatureofJavasincethebeginning,Javawasintendedtobe
usedinbrowsers,anditisusedwidelyinenvironmentswithgraphicaluserinterfaces.Programmersneedtounderstandthreadsmerelytobeabletousethe
asynchronousnatureoftheGUIlibrary.
Thisisnottosaytherearen'tothertimeswhenthreadsareahandyprogrammingtechniqueinJavacertainlyit'seasytouseJavaforaprogramthat
implementsanalgorithmthatnaturallylendsitselftothreading.AndmanyJavaprogramsimplementmultipleindependentbehaviors.Thenextfewsections
coversomeofthecircumstancesinwhichJavathreadsareaneededcomponentoftheprogrameitherdirectlyusingthreadsorusingJavalibrariesthat
makeheavyuseofthreads.Manyofthesecircumstancesareduetotheneedforasynchronousbehaviorortheelegancethatthreadinglendstotheprogram.
Nonblocking I/O
InJava,asinmostprogramminglanguages,whenyoutrytogetinputfromtheuser,youexecutearead()methodspecifyingtheuser'sterminal
(System.ininJava).Whentheprogramexecutestheread()method,theprogramtypicallywaitsuntiltheusertypesatleastonecharacterbeforeit
continuesandexecutesthenextstatement.ThistypeofI/OiscalledblockingI/O:theprogramblocksuntilsomedataisavailabletosatisfytheread()
method.
Thistypeofbehaviorisoftenundesirable.Ifyou'rereadingdatafromanetworksocket,thatdataisoftennotavailablewhenyouwanttoreadit:thedatamay
havebeendelayedintransitoverthenetwork,oryoumaybereadingfromanetworkserverthatsendsdataonlyperiodically.Iftheprogramblockswhenit
triestoreadfromthesocket,it'sunabletodoanythingelseuntilthedataisactuallyavailable.Iftheprogramhasauserinterfacethatcontainsabuttonandthe
userpressesthebuttonwhiletheprogramisexecutingtheread()method,nothinghappens:theprogramisunabletohandlethemouseeventsandexecute
theeventprocessingmethodassociatedwiththebutton.Thiscanbeveryfrustratingfortheuser,whothinkstheprogramhashung.
Traditionally,threetechniquesareavailabletohandlethissituation:
I/OMultiplexing
Developersoftentakeallinputsourcesanduseasystemcalllikeselect()tonotifythemwhendataisavailablefromaparticularsource.Thisallows
inputtobehandledmuchlikeaneventfromtheuser(infact,manygraphicaltoolkitsusethismethodtransparentlytothedeveloper,whosimplyregisters
acallbackfunctionthatiscalledwheneverdataisavailablefromaparticularsource).
BeginningwithJDK1.4,thisfeatureisprovidedwiththeNIOlibraryalibrarythatallowsaprogrammertodealwithI/Oinanasynchronousmanner.
Polling
Pollingallowsadevelopertotestifdataisavailablefromaparticularsource.Ifdataisavailable,thedatacanbereadandprocessed:ifitisnot,the
programcanperformanothertask.Pollingcanbedoneeitherexplicitlywithasystemcalllikepoll()or,insomesystems,bymakingtheread()
functionreturnanindicationthatnodataisimmediatelyavailable.
PollingisalsosupportedbytheNIOlibraryofJDK1.4.InthetraditionalI/Olibrary,thereisonlylimitedsupportforpollingviatheavailable()
methodoftheFilterInputStreamclass.Unfortunately,thismethoddoesnothavetherichsemanticsthatpollingtypicallyhasinmostoperating
systemsandisnotrecommendedasareliabletechniquetodeterminewhetherdataisactuallyavailable.
Signals
Afiledescriptorrepresentingtheinputsourcecanoftenbesetsothatanasynchronoussignalisdeliveredtotheprogramwhendataisavailableonthat
inputsource.Thissignalinterruptstheprogram,whichprocessesthedataandthenreturnstowhatevertaskithadbeendoing.Javadoesnotsupportthis
technique.
WhiletheissueofblockingI/Ocanconceivablyoccurwithanydatasource,itoccursmostfrequentlywithnetworksockets.Ifyou'reusedtoprogramming
sockets,you'veprobablyusedoneofthesetechniquestoreadfromasocket,butperhapsnottowritetoone.Manydevelopers,usedtoprogrammingona
localareanetwork(LAN),arevaguelyawarethatwritingtoasocketmayalsoblock,butit'sapossibilitythatmanyofthemignorebecauseithappensonly
undercertaincircumstances,suchasabacklogingettingdataontothenetwork.ThisbacklograrelyhappensonafastLAN,butifyou'reusingJavato
programsocketsovertheInternet,thechancesofthisbackloghappeningaregreatlyincreased,thusincreasingthechanceofblockingwhileattemptingto
writedataontothenetwork.InJava,youmayneedtwothreadstohandlethesocket:onetoreadfromthesocketandonetowritetoit.
Asaresult,writingaprogramthatusesI/Omeanseitherusingmultiplethreadstohandletraditional(blocking)I/OorusingtheNIOlibrary(orboth).The
NIOlibraryitselfisverycomplexmuchmorecomplexthanthethreadlibrary.Consequently,itisstillofteneasiertosetupaseparatethreadtoreadthedata
(usingtraditionalI/O)fromablockingdatasource.Thisseparatethreadcanblockwhendataisn'tavailable,andtheotherthread(s)intheJavaprogramcan
processeventsfromtheuserorperformothertasks.
Ontheotherhand,therearemanytimeswhentheaddedcomplexityoftheNIOlibraryisworthwhileandwheretheproliferationofthreadsrequiredto
processthousandsofdatasourceswouldbeuntenable.ButusingtheNIOlibrarydoesn'tremoveallthreadingcomplexitiesthatlibraryhasitsownthread
relatedissues.
WeexaminethethreadingissuesrelatedtoI/OindepthinChapter12.
theexactsametechniquetoprovidethefunctionality,buttheyhide(atleastsomeof)thethreadingdetailsfromthedeveloper.Forcompletedetailsonthese
timers,seeChapter11.
Independent Tasks
AJavaprogramisoftencalledontoperformindependenttasks.Inthesimplestcase,asingleappletmayperformtwoindependentanimationsforaweb
page.Amorecomplexprogramwouldbeacalculationserverthatperformscalculationsonbehalfofseveralclientssimultaneously.Ineithercase,whileitis
possibletowriteasinglethreadedprogramtoperformmultipletasks,it'seasierandmoreeleganttoplaceeachtaskinitsownthread.
Thecompleteanswertothequestion"Whythreads?"reallyliesinthiscategory.Asprogrammers,we'retrainedtothinklinearlyandoftenfailtosee
simultaneouspathsthatourprogrammighttake.Butthere'snoreasonwhyprocessesthatwe'veconventionallythoughtofinasinglethreadedfashionneed
necessarilyremainso:whentheSavebuttoninawordprocessorispressed,wetypicallyhavetowaitafewsecondsuntilwecancontinue.Worseyet,the
wordprocessormayperiodicallyperformanautosave,whichinvariablyinterruptstheflowoftypinganddisruptsthethoughtprocess.Inathreadedword
processor,thesaveoperationwouldbeinaseparatethreadsothatitdidn'tinterferewiththeworkflow.Asyoubecomeaccustomedtowritingprogramswith
multiplethreads,you'lldiscovermanycircumstancesinwhichaddingaseparatethreadmakesyouralgorithmsmoreelegantandyourprogramsmore
responsive.
Parallelizable Algorithms
WiththeadventofvirtualmachinesthatcanusemultipleCPUssimultaneously,Javahasbecomeausefulplatformfordevelopingprogramsthatuse
algorithmsthatcanbeparallelizedthatis,runningoneiterationoftheloopononeCPUwhileanotheriterationoftheloopissimultaneouslyrunningon
anotherCPU.Dependenciesbetweenthedatathateachiterationoftheloopneedsmayprohibitaparticularloopfrombeingparallelized,andtheremaybe
otherreasonswhyaloopshouldnotbeparallelized.ButformanyprogramswithCPUintensiveloops,parallelizingtheloopgreatlyspeedsuptheexecution
oftheprogramwhenitisrunonamachinewithmultipleprocessors.
Manylanguageshavecompilersthatsupportautomaticparallelizationofloops,butasyet,Javadoesnot.However,aswe'llseeinChapter15,parallelizinga
loopbyhandisoftennotadifficulttask.
Summary
Inthischapter,we'veprovidedabasicoverviewofwherewe'regoinginourexplorationofthreadedprograms.ThreadingisabasicfeatureofJava,andwe've
seensomeofthereasonswhyit'smoreimportanttoJavathantootherprogrammingplatforms.
Inthenextfewchapters,welookintothebasicsofthreadprogramming.Westartbylookingathowthreadsarecreatedandusedinanapplication.
[ 1 ]
Notetheversionnumberchangeorperhapsweshouldsayleap.ThepredecessortoJ2SE5.0wasJ2SE1.4.Inbeta,J2SE5.0wasalso
knownasJ2SE1.5.Inthisbook,werefertoearlierversionsusingthemorecommonlyusedphraseJDK1.xratherthanJ2SE1.x.
What Is a Thread?
Let'sstartbydiscussingwhatathreadactuallyis.Athreadisanapplicationtaskthatisexecutedbyahostcomputer.Thenotionofataskshouldbefamiliar
toyoueveniftheterminologyisnot.SupposeyouhaveaJavaprogramtocomputethefactorialofagivennumber:
packagejavathreads.examples.ch02.example1;
publicclassFactorial{
publicstaticvoidmain(String[]args){
intn=Integer.parseInt(args[0]);
System.out.print(n+"!is");
intfact=1;
while(n>1)
fact*=n;
System.out.println(fact);
}
}
Whenyourcomputerrunsthisapplication,itexecutesasequenceofcommands.Atanabstractlevel,thatlistofcommandslookslikethis:
Convertargs[0]toaninteger.
Storethatintegerinalocationcalledn.
Printsometext.
Store1inalocationcalledfact.
Testifnisgreaterthan1.
Ifitis,multiplythevaluestoredinfactbythevaluestoredinnanddecrementnby1.
Ifitisn't,printoutthevaluestoredinfact.
Behindthescenes,whathappensissomewhatmorecomplicatedsincetheinstructionsthatareexecutedareactuallymachinelevelassemblyinstructions
eachofourlogicalstepsrequiresmanymachineinstructionstoexecute.Buttheprincipleisthesame:anapplicationisexecutedasaseriesofinstructions.
[ 1 ]
Theexecutionpathoftheseinstructionsisathread.
Consequently,everycomputerprogramhasatleastonethread:thethreadthatexecutesthebodyoftheapplication.InaJavaapplication,thatthreadiscalled
themainthread,anditbeginsexecutingstatementswiththefirststatementofthemain()methodofyourclass.Inotherprogramminglanguages,the
startingpointmaybedifferent,andtheterminologymaybedifferent,butthebasicideaisthesame.
InaJavaprogram,itturnsoutthateveryprogramhasmorethanonethread.Manyofthesearethreadsthatdevelopersareunawareof,suchasthreadsthat
performgarbagecollectionandcompileJavabytecodesintomachinelevelinstructions.Inagraphicalapplication,otherthreadshandleinputfromthemouse
andkeyboardandplayaudio.YourJavaapplicationishighlythreaded,whetheryouprogramadditionalthreadsintoitornot.
Returningtoourexample,let'ssupposethatwewroteaprogramthatperformedtwotasks:onecalculatedthefactorialofanumberandonecalculatedthe
squarerootofthatnumber.Thesearetwoseparatetasks,andsoyoucouldchoosetowritethemastwoseparatethreads.Nowhowwouldyourapplication
run?
Theanswertothatdependsontheconditionsunderwhichtheapplicationisrun.TheJavavirtualmachinenowhastwodistinctlistsofinstructionsto
execute.Onelistcalculatesthefactorialofanumber(asweoutlinedearlier),andtheotherlistcalculatesthesquarerootofthenumber.TheJavavirtual
machineexecutesbothoftheselistsalmostsimultaneously.
Althoughyoumaynothavethoughtaboutitintheseterms,thissituationshouldalsobefamiliartoyoufromthecomputeronwhichyounormallydoyour
work.Theprogramyouusetoreadyouremailisalistofinstructionsthatthecomputerexecutes.Sotooistheprogramthatyouusetolistentomusic.
You'reabletoreademailandlistentomusicatthesametimebecausethecomputerexecutesbothlistsofinstructionsataboutthesametime.
Infact,whathappensisthatthecomputerexecutesahandfulofinstructionsfromtheemailapplicationandthenexecutesahandfulofinstructionsfromthe
musicprogram.Itcontinuesthisprocedure,switchingbackandforthbetweenlistsofinstructions,anditdoesthatquicklyenoughsothatbothprograms
appeartobeexecutingatthesametime.Quicklyenough,infact,thattherearenogapsinthemusic.
IfyouhappentohavemorethanoneCPUonyourcomputer,thelistsofinstructionscanexecuteatexactlythesametime:onelistcanexecuteoneachCPU.
ButmultipleCPUsaren'tnecessarytogivetheappearanceofsimultaneousexecutionortoexploitthepowerofthreading.AsingleCPUcanappearto
executebothlistsofinstructionsinparallel,lettingyoureadyouremailandlistentomusicsimultaneously.
Threadsbehaveexactlythesameway.Inourcase,theJavavirtualmachineexecutesahandfuloftheinstructionstocalculatethefactorialandthenexecutesa
handfulofinstructionstocalculatethesquareroot,andsoon.
Sothreadsaresimplytasksthatyouwanttoexecuteatroughlythesametime.Why,then,writeanapplicationwithmultiplethreads?Whynotjustwrite
multipleapplications?Theanswerliesinthefactthatbecausethreadsarerunninginthesameapplication,theysharethesamememoryspaceinthecomputer.
Thisallowsthemtoshareinformationseamlessly.Youremailprogramandyourmusicapplicationdon'tcommunicateverywell.Atbest,youcancopyand
pastesomedata(likethenameofafile)betweenthetwo.ThatallowsyoutodoubleclickonanMP3attachmentinyouremailandplayitinyourmusic
application,buttheonlyinformationthatissharedbetweenthetwoisthenameoftheMP3file.ThistypeofcooperationisshowninFigure21.
Figure21.Processesinamultitaskingenvironment
Inamultitaskingenvironment,dataintheprogramsisseparatedbydefault:eachhasitsownstackforlocalvariables,andeachhasitsownareaforobjects
andotherdata.Alltheprogramscanaccessvarioustypesofsharedmemory(includingthenameoftheMP3filethatyouclickedoninyouremailprogram).
Thesharedmemoryisrestrictedtoinformationputtherebyotherprograms,andtheAPIstoaccessitareusuallyquitedifferentthantheAPIsusedtoaccess
otherdataintheprogram.
Thistypeofdatasharingisfinefordissimilarprograms,butitisinadequateforotherprograms.Consideranetworkserverthatsendsstockquotesto
multipleclients.Sendingaquotetoaclientisadiscretetaskandmaybedoneinaseparatethread.Infact,iftheclientmustacknowledgethequote,then
sendingthedatainseparatethreadsishighlyrecommended:youdon'twantallclientstowaitforaparticularlyslowclienttorespond.Herethedatatobesent
totheclientsisthesameyoudon'twanteachclienttorequireaseparateserverprocesswhichmustthenreplicateallthedataheldbyeveryotherserver
process.Instead,youwantmultiplethreadsinoneprogramsothattheymaysharedataandeachperformdiscretetasksonthatdata.Thattypeofsharingis
showninFigure22.
Figure22.Threadsinamultithreadedenvironment
Conceptually,thethreadsseemtobethesameasprograms.ThekeydifferencehereisthattheglobalmemoryistheentireJavaheap:threadscan
transparentlyshareaccessbetweenanyobjectintheheap.Eachthreadstillhasitsownspaceforlocalvariables(variablesspecifictothemethodthethreadis
executing).Butobjectsaresharedautomaticallyandtransparently.
Athread,then,isadiscretetaskthatoperatesondatasharedwithotherthreads.
Creating a Thread
Threadscanbecreatedintwoways:usingtheThreadclassandusingtheRunnableinterface.TheRunnableinterface(generally)requiresaninstanceof
athread,sowebeginwiththeThreadclass.
Inthissection,westartdevelopingatypinggame.Theideaofthisgameisthatcharactersaredisplayedandtheusermusttypethekeycorrespondingtothe
character.Throughthenextfewchapters,weaddenoughlogictoscoretheuser'saccuracyandtimingandprovideenoughfeedbacksothattheusercan
improvehertypingskills.
Fornow,wearecontenttodisplayarandomcharacteranddisplaythecharactertheusertypesinresponse.Thisapplicationhastwotasks:onetaskmust
continuallydisplayarandomcharacterandthenpauseforsomerandomperiodoftime.Thesecondtaskmustdisplaycharacterstypedonthekeyboard.
publicinterfaceCharacterSource{
publicvoidaddCharacterListener(CharacterListenercl);
publicvoidremoveCharacterListener(CharacterListenercl);
publicvoidnextCharacter();
}
WewanttousethestandardJavapatternofeventlistenerstohandlethesecharacters:alistenercanregisterwithaparticularsourceandbenotifiedwhena
newcharacterisavailable.ThatrequiresthetypicalsetofJavaclassesforalistenerpattern,startingwiththelistenerinterface:
packagejavathreads.examples.ch02;
publicinterfaceCharacterListener{
publicvoidnewCharacter(CharacterEventce);
}
Theeventsthemselvesareobjectsofthisclass:
packagejavathreads.examples.ch02;
publicclassCharacterEvent{
publicCharacterSourcesource;
publicintcharacter;
publicCharacterEvent(CharacterSourcecs,intc){
source=cs;
character=c;
}
}
Andfinally,weneedahelperclassthatfirestheeventswhenappropriate:
packagejavathreads.examples.ch02;
importjava.util.*;
publicclassCharacterEventHandler{
privateVectorlisteners=newVector();
publicvoidaddCharacterListener(CharacterListenercl){
listeners.add(cl);
}
publicvoidremoveCharacterListener(CharacterListenercl){
listeners.remove(cl);
}
publicvoidfireNewCharacter(CharacterSourcesource,intc){
CharacterEventce=newCharacterEvent(source,c);
CharacterListener[]cl=(CharacterListener[])
listeners.toArray(newCharacterListener[0]);
for(inti=0;i<cl.length;i++)
cl[i].newCharacter(ce);
}
}
Inourgraphicaldisplay,onecanvasregisterstobenotifiedwhentheusertypesacharacterthatcanvasdisplaysthecharacter.Asecondcanvasregisterstobe
notifiedwhenarandomcharacterisgenerateditdisplaysthenewcharactersastheyaregenerated.We'vechosenthisdesignpatternsince,inlaterexamples,
multipleobjectswillbeinterestedinknowingwhennewcharactersaregenerated.
Here'sautilityclassthatcandisplayagivencharacter:
packagejavathreads.examples.ch02;
importjava.awt.*;
importjavax.swing.*;
publicclassCharacterDisplayCanvasextendsJComponentimplementsCharacterListener{
protectedFontMetricsfm;
protectedchar[]tmpChar=newchar[1];
protectedintfontHeight;
publicCharacterDisplayCanvas(){
setFont(newFont("Monospaced",Font.BOLD,18));
fm=Toolkit.getDefaultToolkit().getFontMetrics(getFont());
fontHeight=fm.getHeight();
}
publicCharacterDisplayCanvas(CharacterSourcecs){
this();
setCharacterSource(cs);
}
publicvoidsetCharacterSource(CharacterSourcecs){
cs.addCharacterListener(this);
}
publicDimensionpreferredSize(){
returnnewDimension(fm.getMaxAscent()+10,
fm.getMaxAdvance()+10);
}
publicsynchronizedvoidnewCharacter(CharacterEventce){
tmpChar[0]=(char)ce.character;
repaint();
}
protectedsynchronizedvoidpaintComponent(Graphicsgc){
Dimensiond=getSize();
gc.clearRect(0,0,d.width,d.height);
if(tmpChar[0]==0)
return;
intcharWidth=fm.charWidth((int)tmpChar[0]);
gc.drawChars(tmpChar,0,1,
(d.widthcharWidth)/2,fontHeight);
}
}
Althoughthisclasshasnoreferencestothreads,itstillhasthreadrelatedissues:namely,wehadtousethesynchronizedkeywordforsomeofthe
methods.Thisisbecauseofsomethingknownasaracecondition(seeChapter3).
Nowwecanprogramourfirsttask(andourfirstthread):athreadthatperiodicallygeneratesarandomcharacter.InJava,threadsarerepresentedbyinstances
ofthejava.lang.Threadclass.TheyarecreatedjustlikeanyotherJavaobject,buttheycontainaspecialmethodthattellsthevirtualmachinetobegin
executingthecodeofthethreadasaseparate"list."Here'sapartialAPIoftheThreadclass,showingitsconstructorsanditsexecutionrelatedmethods:
packagejava.lang;
publicclassThreadimplementsRunnable{
publicThread();
publicThread(Runnabletarget);
publicThread(ThreadGroupgroup,Runnabletarget);
publicThread(Stringname);
publicThread(ThreadGroupgroup,Stringname);
publicThread(Runnabletarget,Stringname);
publicThread(ThreadGroupgroup,Runnabletarget,Stringname);
publicThread(ThreadGroupgroup,Runnabletarget,Stringname,
longstackSize);
publicvoidstart();
publicvoidrun();
}
Asyousee,threadsarecreatedwithfourpiecesofinformation:
Threadname
Thenameofathreadispartoftheinformationshownwhenathreadobjectisprinted.Otherwise,ithasnosignificance,sogiveyourthreadsnamesthat
makesensetoyouwhenyouseethemprinted.ThedefaultnameforathreadisThreadN,whereNisauniquenumber.
Runnabletarget
Wediscussrunnablesindepthlaterinthischapter.Arunnableobjectisthelistofinstructionsthatthethreadexecutes.Bydefault,thisistheinformation
intherun()methodofthethreaditself.NotethattheThreadclassitselfimplementstheRunnableinterface.
Threadgroup
Threadgroupsareanadvancedtopic(seeChapter13).Forthevastmajorityofapplications,threadgroupsareunimportant.Bydefault,athreadis
assignedtothesamethreadgroupasthethreadthatcallstheconstructor.
Stacksize
Everythreadhasastackwhereitstorestemporaryvariablesasitexecutesmethods.Everythingrelatedtothestacksizeofathreadisplatformdependent:
itsdefaultstacksize,therangeoflegalvaluesforthestacksize,theoptimalvalueforthestacksize,andsoon.Useofthestacksizeinportableprograms
ishighlydiscouraged.Formoreinformation,seeChapter13.
WecanusethesemethodsoftheThreadclasstocreateourfirstthread:
packagejavathreads.examples.ch02.example2;
importjava.util.*;
importjavathreads.examples.ch02.*;
publicclassRandomCharacterGeneratorextendsThreadimplementsCharacterSource{
staticchar[]chars;
staticStringcharArray="abcdefghijklmnopqrstuvwxyz0123456789";
static{
chars=charArray.toCharArray();
}
Randomrandom;
CharacterEventHandlerhandler;
publicRandomCharacterGenerator(){
random=newRandom();
handler=newCharacterEventHandler();
}
publicintgetPauseTime(){
return(int)(Math.max(1000,5000*random.nextDouble()));
}
publicvoidaddCharacterListener(CharacterListenercl){
handler.addCharacterListener(cl);
}
publicvoidremoveCharacterListener(CharacterListenercl){
handler.removeCharacterListener(cl);
}
publicvoidnextCharacter(){
handler.fireNewCharacter(this,
(int)chars[random.nextInt(chars.length)]);
}
publicvoidrun(){
for(;;){
nextCharacter();
try{
Thread.sleep(getPauseTime());
}catch(InterruptedExceptionie){
return;
}
}
}
}
ThefirstthingtonoteaboutthisexampleisthatitextendstheThreadclass.Theclassitselfisconstructedsimplybycallingits(only)constructor,andthe
actuallistofinstructionswewanttoexecuteisintherun()method.Therun()methodisastandardmethodoftheThreadclassitistheplacewherethe
threadbeginsitsexecution.
Inasense,therun()methodissimilartothemain()methodofastandaloneJavaapplication:themain()methodiswhereyourfirstthreadstarts
executing.Subsequentthreadsstartexecutingwiththerun()methodofthethread.Thoughsomesubtledifferencesbetweenrun()andmain()exist,
thisisthebestwaytothinkoftherelationshipbetweenthem.
Sowhentherun()methodofthisclassiseventuallycalled,itfiresoffanewcharactertoitslisteners,sleepsforarandomperiodoftimebetween1and5
seconds,andthenrepeatstheprocess(forever,astheloopneverterminates).
Thesecondtaskofourapplicationisresponsiblefordisplayingthecharacterstypedatthekeyboard.Itisalsoresponsibleforcreatingandstartingoursecond
thread.Thatcodelookslikethis:
packagejavathreads.examples.ch02.example2;
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
importjavathreads.examples.ch02.*;
publicclassSwingTypeTesterextendsJFrameimplementsCharacterSource{
protectedRandomCharacterGeneratorproducer;
privateCharacterDisplayCanvasdisplayCanvas;
privateCharacterDisplayCanvasfeedbackCanvas;
privateJButtonquitButton;
privateJButtonstartButton;
privateCharacterEventHandlerhandler;
publicSwingTypeTester(){
initComponents();
}
privatevoidinitComponents(){
handler=newCharacterEventHandler();
displayCanvas=newCharacterDisplayCanvas();
feedbackCanvas=newCharacterDisplayCanvas(this);
quitButton=newJButton();
startButton=newJButton();
add(displayCanvas,BorderLayout.NORTH);
add(feedbackCanvas,BorderLayout.CENTER);
JPanelp=newJPanel();
startButton.setLabel("Start");
quitButton.setLabel("Quit");
p.add(startButton);
p.add(quitButton);
add(p,BorderLayout.SOUTH);
addWindowListener(newWindowAdapter(){
publicvoidwindowClosing(WindowEventevt){
quit();
}
});
feedbackCanvas.addKeyListener(newKeyAdapter(){
publicvoidkeyPressed(KeyEventke){
charc=ke.getKeyChar();
if(c!=KeyEvent.CHAR_UNDEFINED)
newCharacter((int)c);
}
});
startButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
producer=newRandomCharacterGenerator();
displayCanvas.setCharacterSource(producer);
producer.start();
startButton.setEnabled(false);
feedbackCanvas.setEnabled(true);
feedbackCanvas.requestFocus();
}
});
quitButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
quit();
}
});
pack();
}
privatevoidquit(){
System.exit(0);
}
publicvoidaddCharacterListener(CharacterListenercl){
handler.addCharacterListener(cl);
}
publicvoidremoveCharacterListener(CharacterListenercl){
handler.removeCharacterListener(cl);
}
publicvoidnewCharacter(intc){
handler.fireNewCharacter(this,c);
}
publicvoidnextCharacter(){
thrownewIllegalStateException("Wedon'tproduceondemand");
}
publicstaticvoidmain(Stringargs[]){
newSwingTypeTester().show();
}
}
Mostofthiscodeis,ofcourse,GUIcode.ThelinestonotewithrespecttotheThreadclassareintheactionPerformed()methodassociatedwiththe
Startbutton.Intheeventcallback,weconstructathreadobject(i.e.,theinstanceoftheRandomCharacterGeneratorclass)likeanyotherJavaobject,and
thenwecallthestart()methodonthatobject.NotethatwedidnotcalltheRandomCharacterGeneratorobject'srun()method.Thestart()
methodoftheThreadclasscallstherun()method(seeSection2.3).
Otherthreadsareinvolvedinthisexample,eventhoughyoudon'tseereferencestothem.First,thereisthemainthreadoftheapplication.Thisthreadstarts
whenyoubeginexecutionoftheprogram(i.e.,whenyoutypethejavacommand).Thatthreadcallsthemain()methodofyourapplication.
ThesecondthreadoftheapplicationistheinstanceoftheRandomCharacterGeneratorclass.ItiscreatedthefirsttimetheStartbuttonispressed.
Athirdthreadintheapplicationistheeventprocessingthread.ThatthreadisstartedbytheSwingtoolkitwhenthefirstGUIelementoftheapplicationis
created.Thatthreadissignificanttousbecausethat'sthethreadthatexecutestheactionPerformed()andkeyPressed()methodsoftheapplication.
Therearemanyotherthreadsinthevirtualmachinethatwedon'tinteractwithfornow,we'reconcernedaboutthethreethreadswe'vejustdiscussed.
Atthispoint,youcancompileandruntheapplication.Usingourmasterantscript,executethiscommand:
piccolo%antch2ex2
TheGUIwindowshowninFigure23appears.AfteryoupresstheStartbutton,charactersappearatrandomintervalsinthetophalfofthewindowasyou
typecharacters,theyappearinthebottomhalfofthewindow.
Figure23.TheSwingTypeTesterwindow
Atthispoint,wecan'tdomuchaboutscoringwhattheusertypes.Thatwouldrequirecommunicationbetweenthetwothreadsoftheprogram,whichisthe
topicofthenextchapter.However,wecanclearupafewthingsinthedisplayaswediscusshowtheRandomCharacterGeneratorthreadruns.
Figure24.Lifecycleofathread
Creating a Thread
Thefirstphaseinthislifecycleisthreadcreation.ThreadsarerepresentedbyinstancesoftheThreadclass,socreatingathreadisdonebycallinga
constructorofthatclass.Inourexample,weusethesimplestconstructoravailabletous.AdditionalconstructorsoftheThreadclassallowyoutospecify
thethread'snameoraRunnableobjecttoserveasthethread'starget.
Allthreadshavenamesthatservetoidentifytheminthevirtualmachine.Bydefault,thatnameconsistsofinformationaboutthethread:itspriority,itsthread
group,andotherthreadinformationwediscussinlaterchapters.Ifyoulike,youcangiveathreadadifferentname,perhapsonethatwillhavemeaningto
youifyouprintitout.
WediscusstheRunnableinterfacelaterinthischapter.
Starting a Thread
Athreadexistsonceithasbeenconstructed,butatthatpointitisnotexecutinganycode.Thethreadisinawaitingstate.
Inthiswaitingstate,otherthreadscaninteractwiththeexistingthreadobject.Variousattributesofthewaitingthreadcanbeset:itspriority,itsname,its
daemonstatus,andsoon.We'llseeexamplesofthesethroughoutthebook,buteachoftheseattributesissetsimplybycallingamethodonthewaiting
thread.Therefore,eventhoughthethreadiswaiting,itsstatemaybechangedbyotherthreads.
Whenyou'rereadyforthethreadtobeginexecutingcode,youcallitsstart()method.Thismethodperformssomeinternalhousekeepingandcallsthe
thread'srun()method.Whenthestart()methodreturns,twothreadsarenowexecutinginparallel:theoriginalthread(whichhasreturnedfromcalling
thestart()method)andthenewlystartedthread(whichisnowexecutingitsrun()method).
Afteritsstart()methodhasbeencalled,thenewthreadissaidtobealive.Infact,theThreadclasshasanisAlive()methodthattellsyouthestateof
thethread:iftheisAlive()methodreturnstrue,thethreadhasbeenstartedandisexecutingitsrun()method.IftheisAlive()methodreturnsfalse,
however,thethreadmaynotbestartedyetormaybeterminated.
Terminating a Thread
Oncestarted,athreadexecutesonlyonemethod:therun()method.Therun()methodmaybeverycomplicated,itmayexecuteforever,anditmaycall
millionsofothermethods.Regardless,oncetherun()methodfinishesexecuting,thethreadhascompleteditsexecution.LikeallJavamethods,therun()
methodfinisheswhenitexecutesareturnstatement,whenitexecutesthelaststatementinitsmethodbody,orwhenitthrowsanexception(orfailstocatch
anexceptionthrowntoit).
Asaresult,theonlywaytoterminateathreadistoarrangeforitsrun()methodtocomplete.IfyoulookatthedocumentationoftheThreadclass,you
noticethattheclasscontainsastop()methodwhichseemslikeitmightbeusedtoterminateathread.Itturnsoutthatthestop()methodhasaninherent
problem(aninternalracecondition,seeChapter3).Asaresult,thestop()methodisdeprecatedandshouldnotbeused.SomeJavaimplementations
prohibititsusedirectly,andthesecuritymanagercanalsobeusedtoprohibitprogramsfromcallingit.
Therearemanythreadsthatyoudon'tneedtostop.Often,threadsareperformingafixedtask,andyoualwayswantthetasktoruntocompletion.Inother
cases,suchasourexample,thethreadcanrununtiltheapplicationexits(e.g.,whenwecalltheSystem.exit()methodinresponsetotheuserpressingthe
Quitbutton).
Often,however,youwantathreadtocontinuetoexecuteuntilsomeotherconditionismet.Inourtypinggame,wemightwantone
RandomCharacterGeneratorthreadtoterminatesothatwecanstartadifferentone(perhapsonewithadifferentsetofcharactersavailabletoit).We
exploresomebasicwaystoarrangeforathreadtostoplaterinthischapter.
Therun()methodcannotthrowacheckedexception,butlikeallJavamethods,itcanthrowanuncheckedexception.Throwinganuncheckedexception(an
exceptionthatextendstheRuntimeExceptionclass)orfailingtocatcharuntimeexceptionthrownbysomethingtherun()methodhascalledalso
causesathreadtostop.Threadscanarrangeforspecialexceptionprocessingintheirterminationfordetails,seeChapter13.
RandomCharacterGeneratorthread.Whenathreadexecutesthesleep()method,itpausesforagivennumberofmilliseconds(ormillisecondsplus
nanoseconds),duringwhichitissaidtobeasleep.Whenthepausetimehaselapsed,thethreadwakesupandcontinuesexecutionwiththestatements
immediatelyfollowingthesleep()method.
Strictlyspeaking,sleepingisnotthesamethingasthreadsuspension.Oneimportantdifferenceisthatwithtruethreadsuspension,onethreadwouldsuspend
(andlaterresume)anotherthread.Conversely,thesleep()methodaffectsonlythethreadthatexecutesitit'snotpossibletotellanotherthreadtogoto
sleep.
ThreadscanusethewaitandnotifymechanismdiscussedinChapter4toachievethefunctionalityofthreadsuspensionandresumption.Thedifferenceis
thatthethreadsmustbecodedtousethattechnique(ratherthanagenericsuspend/resumemechanismthatcouldbeimposedfromotherthreads).
Thread Cleanup
Athreadthathascompleteditsrun()methodhasterminated.Itisnolongeractive(theisAlive()methodreturnsfalse).However,thethreadobjectitself
maybeholdinginterestinginformation.Aslongassomeotheractiveobjectholdsareferencetotheterminatedthreadobject,otherthreadscanexecute
methodsontheterminatedthreadandretrievethatinformation.Ifthethreadobjectrepresentingtheterminatedthreadgoesoutofscope,thethreadobjectis
garbagecollected.Onsomeplatforms,thisalsohastheeffectofcleaningupsystemresourcesassociatedwiththethread.
Ingeneral,then,youshouldnotholdontothreadreferencessothattheymaybecollectedwhenthethreadterminates.
Onereasontoholdontoathreadreferenceistodeterminewhenithascompleteditswork.Thatcanbeaccomplishedwiththejoin()method.Thejoin()
methodisoftenusedwhenyouhavestartedthreadstoperformdiscretetasksandwanttoknowwhenthetaskshavecompleted.You'llseethattechniquein
useintheexamplesinChapter15.
Thejoin()methodblocksuntilthethreadhascompleteditsrun()method.Ifthethreadhasalreadycompleteditsrun()method,thejoin()method
returnsimmediately.Thismeansthatyoumaycallthejoin()methodanynumberoftimestoseewhetherathreadhasterminated.Beaware,though,that
thefirsttimeyoucallthejoin()method,itblocksuntilthethreadhasactuallycompleted.Youcannotusethejoin()methodtopollathreadtoseeifit's
running(instead,usetheisAlive()methodjustdiscussed).
Setting a Flag
Themostcommonwayofstoppingathreadistosetsomeinternalflagtosignalthatthethreadshouldstop.Thethreadcanthenperiodicallyquerythatflag
todetermineifitshouldexit.
WecanrewriteourRandomCharacterGeneratorthreadtofollowthisapproach:
packagejavathreads.examples.ch02.example3;
...
publicclassRandomCharacterGeneratorextendsThreadimplementsCharacterSource{
...
privatevolatilebooleandone=false;
...
publicvoidrun(){
while(!done){
...
}
}
publicvoidsetDone(){
done=true;
}
}
Herewe'vecreatedthebooleanflagdonetosignalthethreadthatitshouldquit.Nowinsteadofloopingforever,therun()methodexaminesthestateofthat
[ 2 ]
variableoneveryloopandreturnswhenthedoneflaghasbeenset.Thatterminatesthethread.
Wemustnowmodifyourapplicationtosetthisflag:
packagejavathreads.examples.ch02.example3;
...
publicclassSwingTypeTesterextendsJFrameimplementsCharacterSource{
...
privateJButtonstopButton;
...
privatevoidinitComponents(){
...
stopButton=newJButton();
stopButton.setLabel("Stop");
p.add(stopButton);
...
stopButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
startButton.setEnabled(true);
stopButton.setEnabled(false);
producer.setDone();
feedbackCanvas.setEnabled(false);
}
});
...
}
...
}
Nowwehavetwobuttons:aStartandaStopbutton.WhentheStopbuttonispressed,thesetDone()methodiscalled,andthenexttimethe
RandomCharacterGeneratorthreadexecutesthetopofitsloop,thatthreadexits.ThisprocessalsoreenablestheStartbutton:wecanstartanewthread
atanytime.
Thisraisesaninterestingdesignquestion:isitbettertocreateanewthreadlikethis,orwoulditbebettersomehowtosuspendtheexistingthreadandresume
itwhenwe'reready?Ofcourse,wedon'tyethavethetoolsnecessarytoprogramthesuspensionandresumptionofthethread,sothat'sthereasonwe'vedone
itthisway.Itwouldbemorenaturalsimplytosuspendandresumethethread,aswedoinChapter4.
However,inacaselikethis,itactuallydoesnotmatter.Inourexperience,developersbecometoohungupontheperceivedperformancepenaltiesthey
attributetocreatingathread.Ifyou'rewritingaprogramanditiseasiertoabandonathreadandcreateanewoneratherthanreusinganexistingone,inmost
casesthat'swhatyoushoulddo.WerevisitthistopicinmoredepthwhenwediscussthreadpoolsinChapter10andthreadperformanceinChapter14.
CallingthesetDone()methodisasimplewayforthreadstocommunicatewitheachother.Threadsmustusespecialrulesforcommunicationlikethis(see
Chapter3).Ingeneral,though,threadscancallmethodsoneachother,aswellasaccessingthesameobjects,topassinformationbetweenthemselves.
Interrupting a Thread
ThelastexamplehasadelaybetweenwhentheactionPerformed()methodcalledthesetDone()methodandtheRandomCharacterGenerator
threadexited.Delaysofsomesortwhenarrangingforathreadtoterminateareinevitable,butsometimesthedelayneedstobeminimized.
Inourexample,thedelayoccursbecausetheRandomCharacterGeneratorthreadexecutessomenumberofstatementsafterthesetDone()methodis
calledandbeforeitchecksthevalueofthedonevariable.Intheworstcase,theeventthreadexecutingtheactionPerformed()methodcallsthe
setDone()methodjustaftertheRandomCharacterGeneratorthreadchecksthevalueofthedonevariable.Then,eventhoughit'sdone,theloopgets
anewcharacteroutofthearray,printsittothescreen,andgoestosleepforsomeamountoftime.Finallyitwakesup,returnstothetopoftheloop,seesthat
thedonevariablehasbeensettotrue,andreturns.
Thedelayinthiscaseisminimal,butit'slikelytobeclosetotheamountoftimethattheRandomCharacterGeneratorthreadissleeping(sincetheother
operationsareveryshort).Ifweoriginallyspecifya15seconddelay,weprobablywon'twanttowaittheentire15secondsbeforethethreadterminates.
Inothercases,thedelaycanbeworse:ifthethreadisexecutingaread()methodtoobtaindatafromasocket,thedatamaynevercome.Orthethreadmay
beexecutingthewait()method(seeChapter4)andwaitingforaneventthatmaynevercome.Methodslikethesearecalledblockingmethodsbecausethey
blockexecutionofthethreaduntilsomethinghappens(e.g.,theexpirationofthesleep()method).
Whenyouarrangeforathreadtoterminate,youoftenwantittocompleteitsblockingmethodimmediately:youdon'twanttowaitforthedata(orwhatever)
anymorebecausethethreadisgoingtoexitanyway.Youcanusetheinterrupt()methodoftheThreadclasstointerruptanyblockingmethod.
Theinterrupt()methodhastwoeffects.First,itcausesanyblockedmethodtothrowanInterruptedException.Inourexample,thesleep()
methodisablockingmethod.IftheeventprocessingthreadinterruptstheRandomCharacterGeneratorthreadwhilethatthreadisexecutingthe
sleep()method,thesleepmethodimmediatelywakesupandthrowsanInterruptedException.Othermethodsthatbehavethiswayincludethe
wait()method,thejoin()method,andmethodsthatreadI/O(thoughtherearecomplicationswhenhandlingI/O,aswediscussinChapter12).
Thesecondeffectistosetaflaginsidethethreadobjectthatindicatesthethreadhasbeeninterrupted.Toquerythisflag,usetheisInterrupted()
method.Thatmethodreturnstrueifthethreadhasbeeninterrupted.
Here'showathreadusesthisinformationtodeterminewhetherornotitshouldterminate:
packagejavathreads.examples.ch02.example4;
...
publicclassRandomCharacterGeneratorextendsThread{
...
//Note:thedoneinstancevariableandsetDone()methodareremovedfrom
//example2
publicvoidrun(){
while(!isInterrupted()){
...
}
}
}
Thisexampleisalmostexactlythesameastheoneinwhichweuseadoneflagtosignalthatthethreadshouldreturn.Inthiscase,weusetheinterruptedflag
instead.ThatmeanswenolongerneedthesetDone()method.InsteadofcallingthesetDone()method,theactionPerformed()methodassociated
withtheStopbuttoninourapplicationnowdoesthis:
producer.interrupt();
IfthemainthreadexecutesthisstatementwhiletheRandomCharacterGeneratorthreadissleeping,theRandomCharacterGeneratorthreadgets
theinterruptedexceptionandimmediatelyreturnsfromtherun()method.Otherwise,whenthecharacterfeedingthreadnextgetstothetopofitsloop,it
seesthattheinterruptedflaghasbeensetandreturnsfromitsrun()methodthen.Eitherway,therandomcharactergeneratorthreadcompletesitstask.
Notethatthistechniquedoesnotcompletelyeliminatethepossibilitythatwesleepforsomeamountoftimeafterthethreadisaskedtostop.It'spossiblefor
themainthreadtocalltheinterrupt()methodjustaftertheRandomCharacterGeneratorhascalledtheisInterrupted()method.Sothe
characterreadingthreadwillcontinueandexecutethesleep()method:theinterruptwasdeliveredafterthecharacterreadingthreadtestedtheinterrupted
state.Thisisanotherexampleofaraceconditionthatwesolveinthenextchapter.Sincetheraceconditionisbenignforourpurposes(itjustmeanswesleep
onemoretimethanwe'dlike),thisissufficientforourpurposes.Onemoresubtletytobeawareof:iftheinterruptcomesduringthesleep()methodso
thatthecharacterreadingthreaddoesreceivetheInterruptedExceptionthethread'sinterruptedstateis*not*set.Hence,inourexampleweexitthe
run()methodatthatpointratherthanretestingtheisInterrupted()value.
packagejava.lang;
publicinterfaceRunnable{
publicvoidrun();
}
TheRunnableinterfaceallowsyoutoseparatetheimplementationofataskfromthethreadusedtorunthetask.Forexample,insteadofextendingthe
Threadclass,ourRandomCharacterGeneratorclassmighthaveimplementedtheRunnableinterface:
packagejavathreads.examples.ch02.example5;
...
//Note:UseExample3asthebasisforcomparison
publicclassRandomCharacterGeneratorimplementsRunnable{
...
}
ThischangesthewayinwhichthethreadthatrunstheRandomCharacterGeneratorobjectmustbeconstructed:
packagejavathreads.examples.ch02.example5;
...
publicclassSwingTypeTesterextendsJFrameimplementsCharacterSource{
...
privatevoidinitComponents(){
...
startButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
producer=newRandomCharacterGenerator();
displayCanvas.setCharacterSource(producer);
Threadt=newThread(producer);
t.start();
startButton.setEnabled(false);
stopButton.setEnabled(true);
feedbackCanvas.setEnabled(true);
feedbackCanvas.requestFocus();
}
});
...
}
...
}
Nowwemustconstructthethreaddirectlyandpasstherunnableobject(producer)tothethread'sconstructor.Thenwestartthethread(insteadofstarting
therunnableobject).
ThisleadstothequestionofwhetheryoushouldusetheRunnableinterfaceortheThreadclasswhendesigningyourownapplication.Theanswerisyes.
ThetruthisthatsometimesitmakessensetousetheRunnableinterfaceandsometimesitmakessensetousetheThreadclass.Theanswerdependson
whetheryouwouldlikeyournewclasstoinheritbehaviorfromtheThreadclassorifyourclassneedstoinheritfromotherclasses.
IfyouextendtheThreadclassaswedoinourfirstexamples,thenyouinheritthebehaviorandmethodsoftheThreadclass.Thatisveryimportantin
example4,whereweusedtheinterrupt()methodtosignalthattheRandomCharacterGeneratorshouldceaseoperations.Theinterrupt()
methodispartoftheThreadclass,andthereasonwhyweareabletointerrupttheRandomCharacterGeneratorthreadisbecauseitextendsthe
Threadclass.
Infact,weshouldpointoutthatthefullsourcecodeforexample5isbasedonexample3,notexample4.WehavetousethesetDone()methodtosignal
thattherandomcharactergenerator'srun()methodshouldterminatebecausethatclassnolongerhasaninterrupt()method.Ifwestillwanttointerrupt
thesleep()methodoftheRandomCharacterGeneratorclass,thenwemustwritetheSwingTypeTesterclasslikethis:
packagejavathreads.examples.ch02.example6;
...
publicclassSwingTypeTesterextendsJFrameimplementsCharacterSource{
...
privatevoidinitComponents(){
...
stopButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
...
displayThread.interrupt();
}
});
...
}
AsimilarexamplecanbeusedtoshowwhyitissometimespreferabletousetheRunnableinterface.Let'ssupposethatwewantthecharacterinour
displaycanvastomoveacrossthescreenuntiltheusertypesinthematchingcharacter.Thisrequiresanotherthread,onethatcontrolstheanimationofthe
character.Everyfewmilliseconds,thecharacterneedstoberedisplayedonthecanvasjustslightlytotherightofwhereitwaspreviouslydisplayed.This
makesthecharacterappeartobemoving.
Wecoulddevelopabrandnewclasstodothis,butitsharesmostofthelogicoftheexistingCharacterDisplayCanvasclass.ThenewChar()method
issomewhatdifferentandthere'snowsomeanimationlogictodealwith,butclearlyit'sbetterinthisexampleifweextendCharacterDisplayCanvas
(andinheritthemethodsthatsetupthecanvassizeandfont)thanifweextendtheThreadclass.ThisisacasethatcallsfortheRunnableinterface:
packagejavathreads.examples.ch02.example7;
importjava.awt.*;
importjavax.swing.*;
importjavathreads.examples.ch02.*;
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
privatevolatilebooleandone=false;
privateintcurX=0;
publicAnimatedCharacterDisplayCanvas(){
}
publicAnimatedCharacterDisplayCanvas(CharacterSourcecs){
super(cs);
}
publicsynchronizedvoidnewCharacter(CharacterEventce){
curX=0;
tmpChar[0]=(char)ce.character;
repaint();
}
protectedsynchronizedvoidpaintComponent(Graphicsgc){
Dimensiond=getSize();
gc.clearRect(0,0,d.width,d.height);
if(tmpChar[0]==0)
return;
intcharWidth=fm.charWidth(tmpChar[0]);
gc.drawChars(tmpChar,0,1,
curX++,fontHeight);
}
publicvoidrun(){
while(!done){
repaint();
try{
Thread.sleep(100);
}catch(InterruptedExceptionie){
return;
}
}
}
publicvoidsetDone(booleanb){
done=b;
}
}
ThisclassdemonstratesthecanonicaltechniquetohandleanimationinJava:athreadmakessuccessivecallstotherepaint()method,whichinturncalls
thepaintComponent()method.EverytimethepaintComponent()methodiscalled,wedisplaythecharacterwithanewXcoordinatethatisslightly
shiftedtotheright.
Thethreadthatcontrolstheanimationinthiscanvasiscreatedjustasbefore:theactionPerformed()methodoftheStartbuttonneedstocreateanew
thread,passingintheAnimatedCharacterCanvasasitsrunnabletarget.Italsoneedstostartthatthread.Thestop()method,ontheotherhand,calls
thesetDone()methodtoterminatetheanimation.Here'showitlooks:
packagejavathreads.examples.ch02.example7;
...
publicclassSwingTypeTesterextendsJFrameimplementsCharacterSource{
...
privatevoidinitComponents(){
...
startButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
...
displayCanvas.setDone(false);
Threadt=newThread(displayCanvas);
t.start();
...
}
});
stopButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventevt){
displayCanvas.setDone(true);
...
}
});
...
}
...
}
WebeganthissectionbywonderingwhetheritwaspreferabletoprogramataskusingtheRunnableinterfaceortheThreadclass.We'veseenexamplesof
whyyouwouldneedeach.There'sanadditionaladvantagetotheRunnableinterface,however.WithRunnable,Javaprovidesanumberofclassesthat
handlethreadingissuesforyou.Theseclasseshandlethreadpooling,taskscheduling,ortimingissues.Ifyou'regoingtousesuchaclass,yourtaskmustbe
aRunnableobject(or,insomecases,anobjectthathasanembeddedRunnableobject).
IfyoudoathoroughprogramdesignandUnifiedModelingLanguage(UML)modelofyourapplication,theresultingobjecthierarchytellsyouprettyclearly
whetheryourtaskneedstosubclassanotherclass(inwhichcaseyoumustusetheRunnableinterface)orwhetheryouneedtousethemethodsofthe
Threadclasswithinyourtask.Butifyourobjecthierarchyissilentontheparentclassforyourtask,orifyoudoalotofprototypingorextreme
programming,thenwhat?Thenthechoiceisyours:youcanusetheRunnableinterface,whichgivesyoualittlemoreflexibilityatthecostoftheoverhead
ofkeepingtrackofthethreadobjectsseparately,oryoucantradethatflexibilityforsimplicityandsubclasstheThreadclass.
publicclassMyThreadextendsThread{
publicvoidrun(){
if(Thread.currentThread()!=this)
thrownewIllegalStateException(
"Runmethodcalledbyincorrectthread");
...mainlogic...
}
}
Similarly,withinanarbitraryobject,youcanusethecurrentThread()methodtoobtainareferencetoacurrentthread.Thistechniquecanbeusedbya
Runnableobjecttoseewhetherithasbeeninterrupted:
publicclassMyRunnableimplementsRunnable{
publicvoidrun(){
while(!Thread.currentThread().isInterrupted()){
...mainlogic...
}
}
}
Infact,theThreadclassincludesastaticmethodinterrupted()thatreturnsthevalueofThread.currentThread().isInterrupted()italso
clearstheinterruptedflag.You'lloftenseebothuseswithinthreadedprograms.
Summary
Inthischapter,we'vehadourfirsttasteofthreads.We'velearnedthatthreadsareseparatetasksexecutedbyasingleprogram.Thisisthekeytothinking
abouthowtodesignagoodmultithreadedprogram:whatlogicaltasksmakeupyourprogram?Howcanthesetasksbeseparatedtomaketheprogramlogic
easier,orbenefityourprogrambyrunninginparallel?Inourcase,wehavetwosimpletasks:displayarandomcharacteranddisplaythekeythatausertypes
inresponse.Inlaterchapters,weaddmoretasks(andmorethreads)tothislist.
Ataprogramminglevel,we'velearnedhowtoconstruct,start,pause,andstopthreads.We'vealsolearnedabouttheRunnableinterfaceandhowthat
interfaceallowsusagreatdegreeofflexibilityinhowwedeveloptheclasshierarchyforourobjects.TaskscanbeeitherThreadobjectsorRunnable
objectsassociatedwithathread.UsingtheRunnableinterfaceallowsmoreflexibilityinhowyoudefineyourtasks,butbothapproacheshavemeritin
differentsituations.
We'vealsotouchedonhowthreadsinteroperatebycallingmethodsonthesameobject.Theabilityofthreadstointeroperateinthismannerincludesthe
abilityforthemtosharedataaswellascode.Thatdatasharingiskeytothebenefitsofamultithreadedprogram,butitcarrieswithitsomepitfalls.Thisis
coveredinthenextchapter.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
FactorialExample
javathreads.examples.ch02.example1.Factorialnumber
ch2ex1
FirstSwingTypeTester
javathreads.examples.ch02.example2.SwingTypeTester
ch2ex2
TypeTester(withStopbutton)
javathreads.examples.ch02.example3.SwingTypeTester
ch2ex3
TypeTester(usesinterrupt()method)
javathreads.examples.ch02.example4.SwingTypeTester
ch2ex4
TypeTester(usesRunnableinterface)
javathreads.examples.ch02.example5.SwingTypeTester
ch2ex5
TypeTester(Runnableandinterrupt())
javathreads.examples.ch02.example6.SwingTypeTester
ch2ex6
TypeTester(animateddisplay)
javathreads.examples.ch02.example7.SwingTypeTester
ch2ex7
ThefactorialprogramacceptsacommandlineargumenttoindicatetheintegerwhosefactorialshouldbecalculatedthatcanbesetwiththisAntproperty:
<propertyname="FactorialArg"value="10"/>
[ 1 ]
Don'tgethunguponthestrictsequentialorderingofthelist.Asaconcept,thinkingofathreadasanorderedlistofinstructions
makesalotofsense,buttheorderingcanchangeundercertaincircumstances(seeChapter5).
[ 2 ]
We'vealsointroducedtheuseoftheJavakeywordvolatileforthatvariable.Likethesynchronizedkeyword,itisintrinsically
relatedtothreadprogramming(seeChapter3).
Chapter3.Data Synchronization
Inthepreviouschapter,wecoveredalotofground:weexaminedhowtocreateandstartthreads,howtoarrangeforthemtoterminate,howtonamethem,
howtomonitortheirlifecycles,andsoon.Intheexamplesofthatchapter,however,thethreadsthatweexaminedweremoreorlessindependent:theydidnot
needtosharedatabetweenthem.
Thereweresomeexceptionstothatlastpoint.Insomeexamples,weneededtheabilityforonethreadtodeterminewhetheranotherwasfinishedwithitstask
(i.e.,thedoneflag).Inothers,weneededtochangeacharactervariablethatwasusedintheanimationcanvasthiswasdonebyathreaddifferentthanthe
Swingthreadthatredrawsthecanvas.Weglossedoverthedetailsatthetime,whichmayhavegiventheimplicationthattheyareminorissues.However,we
mustunderstandthatwhentwothreadssharedata,complexitiesarise.Thesecomplexitiesmustbetakenintoconsiderationwhetherwe'reimplementinga
largeshareddatabaseorsimplysharingadoneflag.
Inthischapter,welookattheissueofsharingdatabetweenthreads.Sharingdatabetweenthreadscanbeproblematicduetowhatisknownasarace
conditionbetweenthreadsthatattempttoaccessthesamedatamoreorlesssimultaneously(i.e.,concurrently).Inthischapter,weexaminetheconceptofa
raceconditionandmechanismsthatsolvetheracecondition.Wewillseehowthesemechanismscanbeusedtocoordinateaccesstodataaswellassolve
someotherproblemsinthreadcommunication.
packagejavathreads.examples.ch02.example7;
privatevolatilebooleandone=false;
privateintcurX=0;
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
...
publicsynchronizedvoidnewCharacter(CharacterEventce){
curX=0;
tmpChar[0]=(char)ce.character;
repaint();
}
protectedsynchronizedvoidpaintComponent(Graphicsgc){
Dimensiond=getSize();
gc.clearRect(0,0,d.width,d.height);
if(tmpChar[0]==0)
return;
intcharWidth=fm.charWidth(tmpChar[0]);
gc.drawChars(tmpChar,0,1,
curX++,fontHeight);
}
publicvoidrun(){
while(!done){
repaint();
try{
Thread.sleep(100);
}catch(InterruptedExceptionie){
return;
}
}
}
publicvoidsetDone(booleanb){
done=b;
}
}
Thisexamplehasmultiplethreads.Themostobviousistheonethatwecreatedandwhichexecutestherun()method.Thatthreadisspecificallycreatedto
wakeupevery0.1secondstosendarepaintrequesttothesystem.Tofulfilltherepaintrequest,thesystematalatertimeandinadifferentthread(the
eventdispatchingthread,tobeprecise)callsthepaintComponent()methodtoadjustandredrawthecanvas.Thisconstantadjustmentandredrawingis
whatisseenasanimationbytheuser.
Thereisnoraceconditionbetweenthesethreadssincenodatainthisobjectissharedbetweenthem.However,aswementionedattheendofthelastchapter,
otherthreadsinvokemethodsofthisobject.Forexample,thenewCharacter()methodiscalledfromtherandomcharactergeneratingthread(acharacter
source)wheneverthecharactertobetypedchanges.
Inthiscase,thereisaracecondition.ThethreadthatcallsthenewCharacter()methodisaccessingthesamedataasthethreadthatcallsthe
paintComponent()method.Therandomcharactergeneratingthreadmaychangethecharacterwhiletheeventdispatchingthreadisusingit.Both
threadsarealsochangingtheXlocationthatspecifieswherethecharacteristobedrawn.
AraceconditionexistsbecausethepaintComponent()andnewCharacter()methodsarenotatomic.ItispossibleforthenewCharacter()method
tochangethevaluesofthetmpCharandcurXvariableswhilethepaintComponent()methodisusingthem.OrforthenewCharacter()and
paintComponent()methodstoleavethecurXvariableinastatethatdependsonwhichindividualinstructionsofthetwothreadsareexecutedfirst.We
examineraceconditionsinmoredetaillaterfornow,wejusthavetounderstandthatraceconditionscangeneratedifferentresults,includingunexpected
results,thataredependentonexecutionorder.
TheJavaspecificationprovidescertainmechanismsthatdealspecificallywiththisproblem.TheJavalanguageprovidesthesynchronizedkeywordin
comparisonwithotherthreadingsystems,thiskeywordallowstheprogrammeraccesstoaresourcethatisverysimilartoamutexlock.Forourpurposes,it
simplypreventstwoormorethreadsfromcallingthemethodsofthesameobjectatthesametime.
BydeclaringthenewCharacter()andpaintComponent()methodssynchronized,weeliminatetheracecondition.Ifonethreadwantstocalloneof
thesemethodswhileanotherthreadisalreadyexecutingoneofthem,thesecondthreadmustwait:thefirstthreadgetstocompleteexecutionofitsmethod
beforethesecondthreadcanexecuteitsmethod.Sinceonlyonethreadgetstocalleithermethodatatime,onlyonethreadatatimeaccessesthedata.
Underthecovers,theconceptofsynchronizationissimple:whenamethodisdeclaredsynchronized,thethreadthatwantstoexecutethemethodmust
acquireatoken,whichwecallalock.Oncethemethodhasacquired(orcheckedoutorgrabbed)thislock,itexecutesthemethodandreleases(orreturns)the
lock.Nomatterhowthemethodreturnsincludingviaanexceptionthelockisreleased.Thereisonlyonelockperobject,soiftwoseparatethreadstryto
callsynchronizedmethodsofthesameobject,onlyonecanexecutethemethodimmediatelytheotherhastowaituntilthefirstthreadreleasesthelockbefore
itcanexecutethemethod.
Theproblematthispointrelatestothescopeofthelock:thescopeoftherun()methodistoolarge.Bysynchronizingtherun()method,thelockis
grabbedandneverreleased.Thereisawaytoshrinkthescopeofalockbysynchronizingonlytheportionoftherun()methodthatprotectsthedoneflag
(whichweexaminelaterinthischapter).However,thereisamoreelegantsolutioninthiscase.
ThesetDone()methodperformsonlyoneoperationwiththedoneflag:itstoresavalueintotheflag.Therun()methodalsoperformsoneoperation
withthedoneflag:itreadsthevalueduringeachiterationoftheloop.Furthermore,itdoesnotmatterifthevaluechangesduringtheiterationofthese
methods,aseachloopmustcompleteanyway.
Theissuehereisthatwepotentiallyhavearaceconditionbecauseonepieceofdataisbeingsharedbetweentwodifferentthreads.Inourfirstexample,the
raceconditioncameaboutbecausethethreadswereaccessingmultiplepiecesofdataandtherewasnowaytoupdateallofthematomicallywithoutusingthe
synchronizedkeyword.Whenonlyasinglepieceofdataisinvolved,thereisadifferentsolution.
Javaspecifiesthatbasicloadingandstoringofvariables(exceptforlonganddoublevariables)isatomic.Thatmeansthevalueofthevariablecan'tbefound
inaninterimstateduringthestore,norcanitbechangedinthemiddleofloadingthevariabletoaregister.ThesetDone()methodhasonlyonestore
operationtherefore,itisatomic.Therun()methodhasonlyonereadoperation.Sincetherestoftherun()methoddoesnotdependonthevalueofthe
variableremainingconstant,theraceconditionshouldnotexistinthiscase.
Unfortunately,Java'smemorymodelisabitmorecomplex.Threadsareallowedtoholdthevaluesofvariablesinlocalmemory(e.g.,inamachineregister).
Inthatcase,whenonethreadchangesthevalueofthevariable,anotherthreadmaynotseethechangedvariable.Thisisparticularlytrueinloopsthatare
controlledbyavariable(likethedoneflagthatweareusingtoterminatethethread):theloopingthreadmayhavealreadyloadedthevalueofthevariableinto
aregisteranddoesnotnecessarilynoticewhenanotherthreadchangesthevariable.
Onewaytosolvethisproblemistoprovidesetterandgettermethodsforthevariable.Wecanthensimplysynchronizeaccessbyusingthesynchronized
keywordonthesemethods.Thisworksbecauseacquiringasynchronizationlockmeansthatalltemporaryvaluesstoredinregistersareflushedtomain
memory.However,Javaprovidesamoreelegantsolution:thevolatilekeyword.Ifavariableismarkedasvolatile,everytimethevariableisusedit
mustbereadfrommainmemory.Similarly,everytimethevariableiswritten,thevaluemustbestoredinmainmemory.Sincetheseoperationsareatomic,
wecanavoidtheraceconditioninourexamplebymarkingourdoneflagasvolatile.
InmostreleasesofthevirtualmachinepriortoJDK1.2,theactualimplementationofJava'smemorymodelmadeusingvolatilevariablesamootpoint:
variableswerealwaysreadfrommainmemory.InsubsequentiterationsofJava,uptoandincludingJ2SE5.0,implementationsofvirtualmachinesbecame
moresophisticatedandintroducednewmemorymodelsandoptimizations:thistrendisexpectedtocontinueinfutureversionsofJava.Withallmodern
virtualmachineimplementations,developerscannotassumethatvariableswillbeaccesseddirectlyfrommainmemory.
Sowhyisvolatilenecessary?Orevenuseful?VolatilevariablessolveonlytheproblemintroducedbyJava'smemorymodel.Theycanbeusedonly
whentheoperationsthatusethevariableareatomic,meaningthemethodsthataccessthevariablemustuseonlyasingleloadorstore.Ifthemethodhas
othercode,thatcodemaynotdependonthevariablechangingitsvalueduringitsoperation.Forexample,operationslikeincrementanddecrement(e.g.,++
and)can'tbeusedonavolatilevariablebecausetheseoperationsaresyntacticsugarforaload,change,andastore.
Aswementioned,wecouldhavesolvedthisproblembyusingsynchronizedsetterandgettermethodstoaccessthevariable.However,thatwouldbefairly
complex.Wemustinvokeanothermethod,includingsettingupparametersandthereturnvariable.Wemustgrabandreleasethelocknecessarytoinvokethe
method.Andallforasinglelineofcode,withoneatomicoperation,thatiscalledmanytimeswithinaloop.Theconceptofusingadoneflagiscommon
enoughthatwecanmakeaverystrongcaseforthevolatilekeyword.
Therequirementsofusingvolatilevariablesseemoverlyrestrictive.Aretheyreallyimportant?Thisquestioncanleadtoanunendingdebate.Fornow,itis
bettertothinkofthevolatilekeywordasawaytoforcethevirtualmachinenottomaketemporarycopiesofavariable.Whilewecanagreethatyou
mightnotusethesetypesofvariablesinmanycases,theyareanoptionduringprogramdesign.InChapter5,weexaminesimilarvariables(atomicvariables)
thatarelessrestrictive:variablesthatarenotonlyatomicbutcanbebuiltonusingprogrammingtechniques.Thisallowsustobuildcomplexatomic
functionality.
Howdoesvolatileworkwitharrays?Declaringanarrayvolatilemakesthearrayreferenceitselfvolatile.Theelementswithinthearrayarenot
volatilethevirtualmachinemaystillstorecopiesofindividualelementsinlocalregisters.Thereisnowaytospecifythattheelementsofanarrayshouldbe
treatedasvolatile.Consequently,ifmultiplethreadsaregoingtoaccessarrayelements,theymustusesynchronizationinordertoprotectthedata.Atomic
variablescanalsohelpinthissituation.
packagejavathreads.examples.ch03.example1;
importjavax.swing.*;
importjava.awt.event.*;
importjavathreads.examples.ch03.*;
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
privatevolatileintscore=0;
privateintchar2type=1;
privateCharacterSourcegenerator=null,typist=null;
publicScoreLabel(CharacterSourcegenerator,CharacterSourcetypist){
this.generator=generator;
this.typist=typist;
if(generator!=null)
generator.addCharacterListener(this);
if(typist!=null)
typist.addCharacterListener(this);
}
publicScoreLabel(){
this(null,null);
}
publicsynchronizedvoidresetGenerator(CharacterSourcenewGenerator){
if(generator!=null)
generator.removeCharacterListener(this);
generator=newGenerator;
if(generator!=null)
generator.addCharacterListener(this);
}
publicsynchronizedvoidresetTypist(CharacterSourcenewTypist){
if(typist!=null)
typist.removeCharacterListener(this);
typist=newTypist;
if(typist!=null)
typist.addCharacterListener(this);
}
publicsynchronizedvoidresetScore(){
score=0;
char2type=1;
setScore();
}
privatesynchronizedvoidsetScore(){
//Thismethodwillbeexplainedlaterinchapter7
SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
setText(Integer.toString(score));
}
});
}
publicsynchronizedvoidnewCharacter(CharacterEventce){
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator){
if(char2type!=1){
score;
setScore();
}
char2type=ce.character;
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
if(char2type!=ce.character){
score;
}else{
score++;
char2type=1;
}
setScore();
}
}
}
TheheartofthisclassisthenewCharacter()method,whichiscalledfrommultiplecharactersources.Itiscalled,atrandomtimes,bythesource(and
thread)thatgeneratesrandomcharacters.Itisalsocalledbyacharactersourceeverytimetheusertypesacharacter(fromtheeventdispatchingthread).Inour
simplescoringsystem,weincrementthescoreeverytimeacharacterisenteredcorrectlyanddecrementthescoreeverytimeacharacterisenteredincorrectly.
Wealsopenalizetheuserforenteringthesamecorrectcharactermorethanonceorfornotenteringthecorrectcharacterintime.
Interestingly,wedon'tactuallyneedtoknowwhichthreadscallthismethod(ortheothermethodsthataccessthesamedata).Theconditionalcheckinthe
methodisusedtofindoutwhichsourcesentthecharacternotwhichthread.Intermsofthreads,wejustneedtounderstandthatthisandothermethods
maybecalledbydifferentthreads,potentiallyatthesametime.Weneedtounderstandwhatisbeingsharedbetweenthedifferentmethodsoreventhesame
methodiftheyarecalledbydifferentthreads.Forthisclass,theactualscore,thecharacterthatneedstobetyped,andafewvariablesthatholdthecharacter
sourcesforregistrationpurposescomprisetheshareddata.Solvingtheraceconditionsmeanssynchronizingthisdataatthecorrectscope.
Inthiscase,synchronizingatthemethodlevelsolvestheproblem,andmakingthevariablesvolatilewouldnotsolvetheproblem.Sinceitiseasierto
understandtheproblembyexaminingafailurecase,let'squicklyexamineonesuchcase:whatcouldhappenifthenewCharacter()methodwerenot
synchronized.Notethatthisisonlyonecaseofmanyinwhichincorrectsynchronizationwouldleadtoincorrectbehaviorinthisclass.
Theusertypesacharacter,whichhappenstobecorrect.TheeventdispatchingthreadcallsthenewCharacter()method,whichroutestotheelse
statementbecausethesourceisthetypist.Thecharacterisdeterminedtobecorrectandthescoreisincremented.However,beforethechar2type
variablecanbesetto1,indicatingthatthecorrectcharacterhasbeentyped,anotherthreadstartstorun.
TherandomcharactersourcecallsthenewCharacter()method,whichroutestotheifstatement.Sincethechar2typevariableisnotsetto1,the
scoreisdecrementedasapenaltyforfailuretotypethecharactercorrectly.
Therandomcharacterthreadstoresthenewcharacterinthechar2typevariable,thescoreisupdated(viathesetScore()method),andthemethod
returns.
Thefirstthreadsetsthechar2typevariableto1,updatesthescore,andreturnsfromthemethod.
Thiscaseisdependentonaschedulingchangeoccurringatanunfortunatetime.Thekeytounderstandingthisbehavioristorealizethatwhenmultiple
threadsareexecutingtheirownlistofinstructions,theoperatingsystemmayswitchfromonelistofstatements(i.e.,onethread)toanotherlistofstatements
(i.e.,adifferentthread)atanyarbitrarypointintime.Inreality,aschedulingchangemayoccuratmorecomplicatedlocations,suchasinthemiddleofan
instructionthatisnotatomic.Inthatcase,thesymptomsmaybeverycomplicated.Evenwiththissimplefailurecase,wehavemanysymptomsoffailure:
Sincethescoreisbothincrementedanddecremented,theuserisnotgivencreditfortypingthecharactercorrectly.
Thenewcharacterfromtherandomcharactergeneratorislost.Itisactuallysetcorrectly,buttheeventdispatchingthreadincorrectlydeletesitassoonas
thatthreadisallowedtoexecute.
Thecharacterislostonlytothescoringcomponent,nottotheanimationcomponent.Theuseriscorrectlyinformedofthenewcharactertobetypedbut
ispenalizedagainwhenthenewcharacteristypedcorrectly.
TheresetScore()methodalsoaccessesthesamecommondataandthereforealsoneedstobesynchronized.Youmaythinkthisisnotnecessarysince
themethodiscalledonlywhenthegameisrestarted:theotherthreadsarenotrunningthen.TheresetScore(),resetGenerator(),and
resetTypist()methodsarealladministrativemethods:theyareallprobablycalledonlyonceandonlyduringinitialization.Inthiscase,theyarebeing
synchronizedtomaketheclassthreadsafeallowingthemethodstobecalledatanytimeshouldtheprogrammerdecidetousethesemethodslaterinan
unexpectedmanner.
Thisisanimportantpointindesigningclassesforuseinamultithreadedenvironment.Evenifyoubelievethataraceconditioncannotoccurbasedonthe
currentuseoftheclass,defensiveprogrammingprincipleswouldarguethatyoumaketheentireclasssafeforexecutionbymultiplethreads.
ThesetScore()methodillustratesafewinterestingpoints.First,theimplementationofthesetScore()methodusesautilitymethod(the
invokeLater()method)becauseofthreadingissuesrelatedtoSwing.Second,thesetScore()methodrequiresthatthescorevariablebedeclared
volatile(againbecauseofSwingrelatedthreadingissues).TheimplementationofthismethodisexplainedinChapter7,butfornow,we'lljustpointout
thatthemethodallowsSwingcode(e.g.,settingthevalueofthelabelinthisexample)tobeexecutedinathreadsafemanner.
Atthispoint,wemayhaveintroducedmorequestionsthananswers.Sobeforewecontinue,let'strytoanswersomeofthosequestions.
Howcansynchronizingtwodifferentmethodspreventmultiplethreadscallingthosemethodsfromsteppingoneachother?Asstatedearlier,synchronizinga
methodhastheeffectofserializingaccesstothemethod.Thismeansthatitisnotpossibletoexecutethesamemethodinonethreadwhilethemethodis
alreadyrunninginanotherthread.Theimplementationofthismechanismisdonebyalockthatisassignedtotheobjectitself.Thereasonanotherthread
cannotexecutethesamemethodatthesametimeisthatthemethodrequiresthelockthatisalreadyheldbythefirstthread.Iftwodifferentsynchronized
methodsofthesameobjectarecalled,theyalsobehaveinthesamefashionbecausetheybothrequirethelockofthesameobject,anditisnotpossiblefor
bothmethodstograbthelockatthesametime.Inotherwords,eveniftwoormoremethodsareinvolved,theyareneverruninparallelinseparatethreads.
ThisisillustratedinFigure31.Whenthread1andthread2attempttoacquirethesamelock(L1),thread2mustwaituntilthread1releasesthelockbeforeit
cancontinuetoexecute.
Figure31.Acquiringandreleasingalock
Thepointtorememberhereisthatthelockisbasedonaspecificinstanceofanobjectandnotonanyparticularmethodorclass.Assumethatwehavetwo
differentscoringcomponentsthatscorebasedondifferentformulaswe'llcallthesetwoScoreLabelobjectscalledobjectAandobjectB.Onethread
canexecutetheobjectA.newCharacter()methodwhileanotherthreadexecutestheobjectB.resetGenerator()method.Thesetwomethods
canexecuteinparallelbecausethecalltotheobjectA.newCharacter()methodgrabsthelockassociatedwithinstancevariableobjectA,andthecallto
theobjectB.resetGenerator()methodgrabstheobjectlockassociatedwithinstancevariableobjectB.Sincethetwoobjectsaredifferentobjects,
twodifferentlocksaregrabbedbythetwothreads:neitherthreadhastowaitfortheother.
Howdoesasynchronizedmethodbehaveinconjunctionwithanunsynchronizedmethod?Tounderstandthis,wemustrememberthatallsynchronizingdoes
istograbanobjectlock.This,inturn,providesthemeansofallowingonlyonesynchronizedmethodtorunatatime,whichinturnprovidesthedata
protectionthatsolvestheracecondition.Simplyput,asynchronizedmethodtriestograbtheobjectlock,andanunsynchronizedmethoddoesn't.Thismeans
thatunsynchronizedmethodscanexecuteatanytime,byanythread,regardlessofwhetherasynchronizedmethodiscurrentlyrunning.Atanygiven
momentonanygivenobject,anynumberofunsynchronizedmethodscanbeexecuting,butonlyonesynchronizedmethodcanbeexecuting.
Whatdoessynchronizingstaticmethodsdo?Andhowdoesitwork?Throughoutthisdiscussion,wekeeptalkingabout"obtainingtheobjectlock."Butwhat
aboutstaticmethods?Whenasynchronizedstaticmethodiscalled,whichobjectarewereferringto?Astaticmethoddoesnothaveaconceptofthethis
reference.Itisnotpossibletoobtaintheobjectlockofanobjectthatdoesnotexist.Sohowdoessynchronizationofstaticmethodswork?Toanswerthis
question,wewillintroducetheconceptofaclasslock.Justasthereisanobjectlockthatcanbeobtainedforeachinstanceofaclass(i.e.,eachobject),thereis
alockthatcanbeobtainedforeachclass.Werefertothisastheclasslock.Intermsofimplementation,thereisnosuchthingasaclasslock,butitisauseful
concepttohelpusunderstandhowallthisworks.
Whenastaticsynchronizedmethodiscalled,theprogramobtainstheclasslockbeforecallingthemethod.Thismechanismisidenticaltothecaseinwhich
themethodisnotstaticitisjustadifferentlock.Andthislockisusedsolelyforstaticmethods.Apartfromthefunctionalrelationshipbetweenthetwolocks,
theyarenotoperationallyrelatedatall.Thesearetwodistinctlocks.Theclasslockcanbegrabbedandreleasedindependentlyoftheobjectlock.Ifanonstatic
synchronizedmethodcallsastaticsynchronizedmethod,itacquiresbothlocks.
Aswementioned,aclasslockdoesnotactuallyexist.TheclasslockistheobjectlockoftheClassobjectthatmodelstheclass.Sincethereisonlyone
Classobjectperclass,usingthisobjectachievesthesynchronizationforstaticmethods.Forthedeveloper,itisbestenvisionedasfollows.Onlyonethread
canexecuteasynchronizedstaticmethodperclass.Onlyonethreadperinstanceoftheclasscanexecuteanonstaticsynchronizedmethod.Anynumberof
threadscanexecutenonsynchronizedmethodsstaticorotherwise.
Wehaveintroducedtheconceptof"lockscope"butonlytouchedonavoidingascopethatistoolargebylockingonlyspecificmethods.Whatifweneedto
lockspecificblocksofcode?Whatifweneedtolockonlyafewlinesofcode?Dowehavetocreateprivatemethodsthatcancontainaslittleasonelineof
code,justtokeeponelineofcodeatomic?Whatifwewanttodoothertasksifwecan'tobtainthelock?Whatifweonlywanttowaitforaspecificperiodof
timeforalock?Whatifwewantlocksissuedinafashionthatisfair?Whatdoesitmeantobefair?Weanswerthesequestionsintheremainderofthis
chapter.
Explicit Locking
Thepurposeofthesynchronizedkeywordistoprovidetheabilitytoallowserializedentrancetosynchronizedmethodsinanobject.Althoughalmostall
theneedsofdataprotectioncanbeaccomplishedwiththiskeyword,itistooprimitivewhentheneedforcomplexsynchronizationarises.Morecomplex
casescanbehandledbyusingclassesthatachievesimilarfunctionalityasthesynchronizedkeyword.TheseclassesareavailablebeginninginJ2SE5.0,
butalternativesforusewithearlierversionsofJavaareshowninAppendixA.
ThesynchronizationtoolsinJ2SE5.0implementacommoninterface:theLockinterface.Fornow,thetwomethodsofthisinterfacethatareimportanttous
arelock()andunlock().UsingtheLockinterfaceissimilartousingthesynchronizedkeyword:wecallthelock()methodatthestartofthe
methodandcalltheunlock()methodattheendofthemethod,andwe'veeffectivelysynchronizedthemethod.
Thelock()methodgrabsthelock.Thedifferenceisthatthelockcannowbemoreeasilyenvisioned:wenowhaveanactualobjectthatrepresentsthelock.
Thisobjectcanbestored,passedaround,andevendiscarded.Asbefore,ifanotherthreadownsthelock,athreadthatattemptstoacquirethelockwaitsuntil
theotherthreadcallstheunlock()methodofthelock.Oncethathappens,thewaitingthreadgrabsthelockandreturnsfromthelock()method.If
anotherthreadthenwantsthelock,ithastowaituntilthecurrentthreadcallstheunlock()method.Let'simplementourscoringexampleusingthisnew
tool:
packagejavathreads.examples.ch03.example2;
...
importjava.util.concurrent.*;
importjava.util.concurrent.locks.*;
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
privateLockscoreLock=newReentrantLock();
...
publicvoidresetGenerator(CharacterSourcenewGenerator){
try{
scoreLock.lock();
if(generator!=null)
generator.removeCharacterListener(this);
generator=newGenerator;
if(generator!=null)
generator.addCharacterListener(this);
}finally{
scoreLock.unlock();
}
}
publicvoidresetTypist(CharacterSourcenewTypist){
try{
scoreLock.lock();
if(typist!=null)
typist.removeCharacterListener(this);
typist=newTypist;
if(typist!=null)
typist.addCharacterListener(this);
}finally{
scoreLock.unlock();
}
}
publicvoidresetScore(){
try{
scoreLock.lock();
score=0;
char2type=1;
setScore();
}finally{
scoreLock.unlock();
}
}
privatevoidsetScore(){
//Thismethodwillbeexplainedlaterinchapter7
SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
setText(Integer.toString(score));
}
});
}
publicvoidnewCharacter(CharacterEventce){
try{
scoreLock.lock();
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator){
if(char2type!=1){
score;
setScore();
}
char2type=ce.character;
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
if(char2type!=ce.character){
score;
}else{
score++;
char2type=1;
}
setScore();
}
}finally{
scoreLock.unlock();
}
}
}
ThisnewversionoftheScoreLabelclassisverysimilartothepreviousversion.TheimplementationnowdeclaresanobjectthatimplementstheLock
interface:thescoreLockobjectwhichwe'llnowusetosynchronizethemethods.WeinstantiateaninstanceoftheReentrantLockclass,aclassthat
implementstheLockinterface.Insteadofdeclaringmethodsassynchronized,thosemethodsnowcallthelock()methodonentryandtheunlock()
methodonexit.Finally,themethodbodiesarenowplacedintry/finallyclausestohandlepossibleruntimeexceptions.Withthesynchronized
keyword,locksareautomaticallyreleasedwhenthemethodexits.Usinglocks,weneedtocalltheunlock()method:byplacingtheunlock()methodcall
inafinallyclause,weguaranteethemethodiscalledwhenthemethodexits,evenifanunexpectedruntimeexceptionisthrown.
Intermsoffunctionality,thisexampleisexactlythesameasthepreviousexample.Intermsofpossibleenhancements,thereisadifference.Thedifferenceis
thatbyusingalockclass,wecannowutilizeotherfunctionalityfunctionality,asweshallsee,thatcan'tbeaccomplishedbyjustusingthesynchronized
keyword.
Usingalockclass,wecannowgrabandreleasealockwheneverdesired.Wecantestconditionsbeforegrabbingorreleasingthelock.Andsincethelockis
nolongerattachedtotheobjectwhosemethodisbeingcalled,itisnowpossiblefortwoobjectstosharethesamelock.Itisalsopossibleforoneobjectto
havemultiplelocks.Lockscanbeattachedtodata,groupsofdata,oranythingelse,insteadofjusttheobjectsthatcontaintheexecutingmethods.
Lock Scope
Sincewenowhavethelockrelatedclassesavailableinourarsenal,manyofourearlierquestionscannowbeaddressed.Let'sbeginlookingattheissueof
lockscopebymodifyingourScoreLabelclass:
packagejavathreads.examples.ch03.example3;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
publicvoidnewCharacter(CharacterEventce){
if(ce.source==generator){
try{
scoreLock.lock();
//Previouscharacternottypedcorrectly:1pointpenalty
if(char2type!=1){
score;
setScore();
}
char2type=ce.character;
}finally{
scoreLock.unlock();
}
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
try{
scoreLock.lock();
if(char2type!=ce.character){
score;
}else{
score++;
char2type=1;
}
setScore();
}finally{
scoreLock.unlock();
}
}
}
}
Sincethelock()andunlock()methodcallsareexplicit,wecanmovethemanywhere,establishinganylockscope,fromasinglelineofcodetoascope
thatspansmultiplemethodsandobjects.Byprovidingthemeansofspecifyingthescopeofthelock,wecannowmovetimeconsumingandthreadsafecode
outsideofthelockscope.Andwecannowlockatascopethatisspecifictotheprogramdesigninsteadoftheobjectlayout.Inthisexample,wemovedthe
sourcecheckoutsideofthelock,andwealsosplitthelockintwo,oneforeachoftheconditions.
Synchronized Blocks
Itispossibleforthesynchronizedkeywordtolockablockofcodewithinamethod.Itisalsopossibleforthesynchronizedkeywordtospecifythe
objectwhoselockisgrabbedinsteadofusingthelockoftheobjectthatcontainsthemethod.MuchofwhatweaccomplishwiththeLockinterfacecanstill
bedonewiththesynchronizedkeyword.Itispossibletolockatascopethatissmallerthanamethod,anditispossibletocreateanobjectjustsothatit
canbeusedasansynchronizationobject.Wecanimplementourlastexamplejustbyusingthesynchronizedkeyword:
packagejavathreads.examples.ch03.example4;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
//Definitionforscorelockdeleted
...
publicsynchronizedvoidresetGenerator(CharacterSourcenewGenerator){
...
}
publicsynchronizedvoidresetTypist(CharacterSourcenewTypist){
...
}
publicsynchronizedvoidresetScore(){
...
}
privatesynchronizedvoidsetScore(){
...
}
publicvoidnewCharacter(CharacterEventce){
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator){
synchronized(this){
if(char2type!=1){
score;
setScore();
}
char2type=ce.character;
}
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
synchronized(this){
if(char2type!=ce.character){
score;
}else{
score++;
char2type=1;
}
setScore();
}
}
}
}
Thissyntaxofthesynchronizedkeywordrequiresanobjectwhoselockisobtained.ThisissimilartoourscoreLockobjectinthepreviousexample.
Forthisexample,wearelockingwiththesameobjectthatwasusedforthesynchronizationofthemethod:thethisobject.Usingthissyntax,wecannow
lockindividuallinesofcodeinsteadofthewholemethod.Wecanalsosharedataacrossmultipleobjectsbylockingonotherobjectsinstead,suchasthedata
objecttobeshared.
Whatifwewanttodoothertasksifwecan'tobtainthelock?TheLockinterfaceprovidesanoptiontotrytoobtainthelock:thetryLock()method.Itis
similartothelock()methodinthatifitissuccessful,itgrabsthelock.Unlikethelock()method,ifthelockisnotavailable,itdoesnotwait.Instead,it
returnswithabooleanvalueoffalse.Ifthelockisobtained,thereturnvalueisabooleanvalueoftrue.Byinspectingthereturnvalue,wecanroutethethread
toseparatetasks:ifthevaluereturnedisfalse,forinstance,wecanroutethethreadtoperformalternativetasksthatdonotrequireobtainingthelock.
Whatifwewanttowaitonlyforaspecificperiodoftimeforalock?ThetryLock()methodisoverloadedwithaversionthatletsyouspecifythe
maximumtimetowait.Thismethodtakestwoparameters:onethatspecifiesthenumberoftimeunitsandaTimeUnitobjectthatspecifieshowthefirst
parametershouldbeinterpreted.Forexample,tospecify50milliseconds,thelongvalueissetto50andtheTimeUnitvalueissetto
TimeUnit.MILLISECONDS.NewinJ2SE5.0,theTimeUnitclassspecifiestimeinunitsthatareeasiertounderstand.InpreviousversionsofJava,most
timebasedfunctionalityiseitherspecifiedinnanosecondsormilliseconds(dependingonthemethod).
Thismethodissimilartothelock()methodinthatitwaitsforthelock,butonlyforaspecifiedamountoftime.ItissimilartothetryLock()methodin
thatitmayreturnwithoutacquiringthelock:itreturnswithavalueoftrueifthelockisacquiredandfalseifnot.
WhataretheothermethodsoftheLockinterfaceusedfor?Weaddressthemlaterinthisbook,startinginChapter4.Fornow,wecanalreadyseethatthe
functionalityofferedbytheLockinterfaceexceedsthefunctionalityofferedbythesynchronizedkeyword.Byusingexplicitlocks,thedeveloperisfreeto
addressissuesspecifictohisprograminsteadofbeingswampedwithconcurrencyissues.
Nested Locks
OurimplementationofthenewCharacter()methodcouldberefactoredintomultiplemethods.Thisisolatesthegeneratorandtypistlogicintoseparate
methods,makingthecodeeasiertomaintain.
packagejavathreads.examples.ch03.example5;
...
privatesynchronizedvoidnewGeneratorCharacter(intc){
if(char2type!=1){
score;
setScore();
}
char2type=c;
}
privatesynchronizedvoidnewTypistCharacter(intc){
if(char2type!=c){
score;
}else{
score++;
char2type=1;
}
setScore();
}
publicsynchronizedvoidnewCharacter(CharacterEventce){
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator){
newGeneratorCharacter(ce.character);
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
newTypistCharacter(ce.character);
}
}
}
Thetwonewmethods(newGeneratorCharacter()andnewTypistCharacter())aresynchronizedbecausetheyaccessthesharedstateofthe
object.However,inthiscase,synchronizingthemethodsisnottechnicallynecessary.Unliketheothermethodsthataccesstheshareddata,thesemethodsare
privatetheycanbecalledonlyfromothermethodsoftheclass.Withintheclass,theyarecalledonlyfromsynchronizedmethods.So,thereisnoreasonfor
thesemethodstoacquirethelockbecauseallcallstothemethodalreadyownthelock.Yetit'sstillagoodideatosynchronizemethodslikethis.Developers
whomodifythisclassmaynotrealizethattheirnewcodeneedstoobtaintheobjectlockbeforecallingoneofthesenewmethods.
ThereasonthisworksisthatJavadoesnotblindlygrabthelockwhenitenterssynchronizedcode.Ifthecurrentthreadownsthelock,thereisnoreasonto
waitforthelocktobefreedoreventograbthelock.Instead,thecodeinthesynchronizedsectionjustexecutes.Furthermore,thesystemissmartenoughto
notfreethelockifitdidnotinitiallygrabituponenteringthesynchronizedsectionofcode.Thisworksbecausethesystemkeepstrackofthenumberof
recursiveacquisitionsofthelock,finallyfreeingthelockuponexitingthefirstmethod(orblock)thatacquiredthelock.Thisfunctionalityiscallednested
locking.
NestedlocksarealsosupportedbytheReentrantLockclasstheclassthatimplementstheLockinterfacethatwehavebeenusingsofar.Ifalock
requestismadebythethreadthatcurrentlyownsthelock,theReentrantLockobjectjustincrementsaninternalcountofthenumberofnestedlock
requests.Callstotheunlock()methoddecrementthecount.Thelockisnotfreeduntilthelockcountreacheszero.Thisimplementationallowstheselocks
tobehaveexactlylikethesynchronizedkeyword.Note,however,thatthisisaspecificpropertyoftheReentrantLockclassandnotageneralproperty
ofclassesthatimplementtheLockinterface.
WhyisJava'ssupportofnestedlocksimportant?Thiswasasimpleexample.Amorecomplexandverycommonexampleisthatofcrosscalling
methods.Itispossibleforamethodofoneclasstocallmethodsofanotherclass,whichinturnmaycallmethodsoftheoriginalclass.IfJavadidnotsupport
nestedlocksandthemethodsofbothclassesweresynchronizedwecoulddeadlocktheprogram.
Thedeadlockoccursbecausethefinalmethodtriestograbalockthatthecurrentthreadhasalreadygrabbed.Thislockcan'tbefreeduntiltheoriginalmethod
unlocksit,butitcan'tunlockituntilitcompletestheexecutionoftheoriginalmethod.Andtheoriginalmethodcan'tcompleteitsexecutionbecausethefinal
methoddoesnotreturn:itisstillwaitingtograbthelock.
Crosscallingmethodsarecommonandcanbesocomplexthatitmaynotbepossibletoevendetectthem,makingfixingpotentialdeadlocksverydifficult.
Andtherearemorecomplexcasesaswell.Ourexampleusesacallbackmechanismbyusingcharactersourcesandlisteners.Inthiscase,charactersources
andlistenersareconnectedindependentlyofeitherclass:itcanbecomeverycomplexifthelistenersarebeingchangedconstantlyduringoperation.
CrosscallingmethodsandcallbacksareveryprevalentinJava'scorelibraryparticularlythewindowingsystem,withitsdependencyoneventhandlersand
listeners.DevelopingthreadedapplicationsorevenjustusingJava'sstandardclasseswouldbeverydifficultifnestedlockswerenotsupported.
Isitpossibletodetecthowmanytimesalockhasbeenrecursivelyacquired?Itisnotpossibletotellwiththesynchronizedkeyword,andtheLock
interfacedoesnotprovideameanstodetectthenumberofnestedacquisitions.However,thatfunctionalityisimplementedbytheReentrantLockclass:
publicclassReentrantLockimplementsLock{
publicintgetHoldCount();
publicbooleanisLocked();
publicbooleanisHeldByCurrentThread();
publicintgetQueueLength();
}
ThegetHoldCount()methodreturnsthenumberofacquisitionsthatthecurrentthreadhasmadeonthelock.Areturnvalueofzeromeansthatthecurrent
threaddoesnotownthelock:itdoesnotmeanthatthelockisfree.TodetermineifthelockisfreenotacquiredbyanythreadtheisLocked()method
maybeused.
TwoothermethodsoftheReentrantLockclassarealsoimportanttothisdiscussion.TheisHeldByCurrentThread()methodisusedtodetermineif
thethreadisownedbythecurrentthread,andthegetQueueLength()methodcanbeusedtogetanestimateofthenumberofthreadswaitingtoacquire
thelock.Thisvaluethatisreturnedisonlyanestimateduetotheraceconditionthatexistsbetweenthetimethatthevalueiscalculatedandthetimethatthe
valueisusedafterithasbeenreturned.
Deadlock
Wehavementioneddeadlockafewtimesinthischapter,andwe'llexaminetheconceptindetailinChapter6.Fornow,wejustneedtounderstandwhatitis
andwhyitisaproblem.
Simplistically,deadlockoccurswhentwoormorethreadsarewaitingfortwoormorelockstobefreedandthecircumstancesintheprogramaresuchthat
thelocksareneverfreed.Interestingly,itispossibletodeadlockevenifnosynchronizationlocksareinvolved.Adeadlocksituationinvolvesthreadswaiting
forconditionsthisincludeswaitingtoacquirealockandalsowaitingforvariablestobeinaparticularstate.Ontheotherhand,itisnotpossibletodeadlock
ifonlyonethreadisinvolved,asJavaallowsnestedlockacquisition.Ifasingleuserthreaddeadlocks,asystemthreadmustalsobeinvolved.
Let'sexamineasimpleexample.Todothis,werevisitandbreakoneofourclassestheAnimatedCharacterDisplayCanvasclass.Thisclassusesa
doneflagtodeterminewhethertheanimationshouldbestopped.Thepreviousexampleofthisclassdeclaresthedoneflagasvolatile.Thisstepwas
necessarytoallowatomicaccesstothevariabletofunctioncorrectly.Inthisexample,weincorrectlysynchronizethemethods.
packagejavathreads.examples.ch03.example6;
...
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
privatebooleandone=false;
...
protectedsynchronizedvoidpaintComponent(Graphicsgc){
...
}
publicsynchronizedvoidrun(){
while(!done){
repaint();
try{
Thread.sleep(100);
}catch(InterruptedExceptionie){
return;
}
}
}
publicsynchronizedvoidsetDone(booleanb){
done=b;
}
}
Twothreadsareinvolvedhere:thethreadcreatedbythisclassandtheeventdispatchingthreadthateventuallycallsthesetDone()method.Onlyonelockis
sharedbetweenthesethreads:thelockattachedtotheobject(theinstanceoftheAnimatedCharacterDisplayCanvasclass)thatisbeingsynchronized.
Thedoneflagismoreinteresting.Itisadatavariablethattherun()methodusestodeterminewhetheritshouldexit.Inessence,therun()methodis
waitingforthedoneflagtobesettotrue.
Whentheanimationthreadisstarted,theobjectlockisgrabbedbytherun()method.Themethoddoesnotreleasetheobjectlockuntilithascompleted
whichisdeterminedbythedoneflag.Later,theuserpressestheStopbuttonthisgeneratesacalltothesetDone()method.ThesetDone()methodnow
triestoacquiretheobjectlock.Theobjectlockcan'tbeacquireduntiltherun()methodsexits.Therun()methoddoesnotexituntilthedoneflagisset.
Andthedoneflagcan'tbesetuntilthesetDone()methodexecutes.Thisisobviouslyacatch22situation:adeadlockiscreated.
Thisexamplehasotherproblemsaswell.Whenthesystemneedstodrawthecanvas,itcallsthepaintComponent()methodfromtheeventdispatching
thread.ThatthreadmustacquirethelockonthecanvasinordertoexecutethepaintComponent()method.Sincethatlockisalreadyheldbytheanimation
threaditself,thepaintComponent()methodneverhastheopportunitytoexecute.WhenyoupresstheStartbuttonontheapplication,nothinghappens
(otherthantheapplicationbecomingtotallyunresponsiveyou'llhavetopressCtrlCtoquit).
Tofixthisproblem,wereducethescopeofthelockusedbytherun()method.Onewaytodothatisbyintroducinganewsynchronizedmethodthat
accessesthedoneflag:
packagejavathreads.examples.ch03.example7;
...
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
...
publicvoidrun(){
while(!getDone()){
...
}
}
publicsynchronizedbooleangetDone(){
returndone;
}
...
}
Nowthattherun()methodissynchronizedonlywhileitisexecutingthegetDone()method,theothermethodshavetheopportunitytograbtheobject
lock,andtheprogramexecutesasdesired.
Thisisasimpleexample,but,asyoucansee,adeadlockcanoccurevenwithsimpleexamples.Thereasonthatadeadlockisaproblemisobviousit
preventstheapplicationfromexecutingcorrectly.Unfortunately,thereisanotherissuedeadlockscanbeverydifficulttodetect,particularlyasaprogramgets
morecomplex.Makingtheexampleevenslightlymorecomplexcanobscurethedeadlock.Todemonstrate,webreakourapplicationfurtherbyusingexplicit
lockswithintheScoreLabelclass.
packagejavathreads.examples.ch03.example8;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
privateLockadminLock=newReentrantLock();
privateLockcharLock=newReentrantLock();
privateLockscoreLock=newReentrantLock();
...
publicvoidresetGenerator(CharacterSourcenewGenerator){
try{
adminLock.lock();
if(generator!=null)
generator.removeCharacterListener(this);
generator=newGenerator;
if(generator!=null)
generator.addCharacterListener(this);
}finally{
adminLock.unlock();
}
}
publicvoidresetTypist(CharacterSourcenewTypist){
try{
adminLock.lock();
if(typist!=null)
typist.removeCharacterListener(this);
typist=newTypist;
if(typist!=null)
typist.addCharacterListener(this);
}finally{
adminLock.unlock();
}
}
...
publicvoidnewCharacter(CharacterEventce){
try{
scoreLock.lock();
charLock.lock();
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator){
if(char2type!=1){
score;
setScore();
}
char2type=ce.character;
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
if(char2type!=ce.character){
score;
}else{
score++;
char2type=1;
}
setScore();
}
}finally{
scoreLock.unlock();
charLock.unlock();
}
}
publicvoidresetScore(){
try{
charLock.lock();
scoreLock.lock();
score=0;
char2type=1;
setScore();
}finally{
charLock.unlock();
scoreLock.unlock();
}
}
UponexaminingourScoreLabelclass,wegotaverygoodidea.WenoticedthattheresetGenerator()andresetTypist()methodsdon'tchange
thescoreorthecharactertobetyped.Inordertobemoreefficient,wecreatealockjustforthesetwomethodsalockthatisusedonlybythe
administrationmethods.Wefurthercreateaseparatelocktodistinguishthescoreandthecharacterthisisjustincaseweneedtomodifyonevariablewithout
theotheratalaterdate.Thisisagoodideabecauseitreducescontentionforthelocks,whichcanincreasetheefficiencyofourprogram.
Unfortunately,duringimplementationwecreatedaproblem.Likeourpreviousexample,thereisnowadeadlockpresentinthecode.Unliketheprevious
example,itmaynotbedetectedintesting.Infact,itmaynotbedetectedatall,astheresetScore()methodisnotcalledfrequentlyenoughfortheproblem
toshowupintesting.Inourpreviousexample,theproblemmanifesteditselfassoonastheapplicationwasstarted.Inthisexample,theprogramcanrun
correctlyformillionsofiterations,onlytofailinproductionwhentheuserpressestheStoporStartbuttonsinacertainway.Sincethisdeadlockisdependent
onthetimingofthethreads,itmayneverfailonthetestingsystemduetothetimingofthetestscriptsandotherfeaturesoftheunderlyingimplementation.
Ourmorecomplexexamplehasadeadlockthatisnotconsistent,makingdetectionincrediblydifficult.
So,whereisthedeadlock?ItisrelatedtothedifferencesinlockacquisitionbetweentheresetScore()andnewCharacter()methods.The
newCharacter()methodgrabsthescorelockfirstwhiletheresetScore()methodgrabsthecharacterlockfirst.Itisnowpossibleforonemethodto
becalledwhichgrabsonelock,but,beforeitcangrabtheotherlock,theothermethodiscalledwhichgrabstheotherlock.Bothmethodsarewaitingtograb
theotherlockwhileholdingoneofthelocks.
Let'slookatapossiblerunofthisimplementationasoutlinedinFigure32.Thethread(thread1)thatgeneratestherandomcharacterscallsthe
newCharacter()method.Thismethodfirstgrabsthescorelock(L1)andthenisabouttograbthecharacterlock.Atthesametime,theuserpressesthe
Startbutton,generatingacalltotheresetScore()method.Theeventdispatchingthread(thread2)thathandlesthebuttonscallstheresetScore()
method.Thread2grabsthecharacterlock(L2)successfullybutfailstograbthescorelock(L1)itthenwaitsforthescorelocktobereleased.Afterthread
1grabsthescorelock,itthentriestograbthecharacterlock(L2).Sincethecharacterlockisalreadyheld,itwaitsforittobereleased.Thefirstthreadis
waitingforthesecondthreadtoreleasethesecondlockwhilethesecondthreadiswaitingforthefirstthreadtoreleasethefirstlock.Neithercanreleasetheir
respectivelocksuntiltheyareabletoacquiretheotherlock.Thisgeneratesacatch22situation:adeadlockhasoccurred.
Figure32.DeadlockintheScoreLabelclass
Canthesystemsomehowresolvethisdeadlock,justasitisabletoavoidthepotentialdeadlockwhenathreadtriestograbthesamelockagain?
Unfortunately,thisproblemisdifferent.Unlikethecaseofthenestedlocks,whereasinglethreadistryingtograbasinglelockmultipletimes,thiscase
involvestwoseparatethreadstryingtograbtwodifferentlocks.Sinceathreadownsoneofthelocksinvolved,itmayhavealreadymadechangesthatmakeit
impossibleforittofreethelock.Tobeabletofixthisproblematthesystemlevel,Javawouldneedasystemwherethefirstlockcan'tbegrabbeduntilitis
safefromdeadlockorprovideawayforthedeadlocktoberesolvedonceitoccurs.Eithercaseisverycomplexandmaybemorecomplexthanjusthaving
thedeveloperdesigntheprogramcorrectly.
Ingeneral,deadlockscanbeverydifficulttoresolve.Itispossibletohaveadeadlockthatdeveloperscan'tfixwithoutacompletedesignoverhaul.Giventhis
complexity,itisnotpossible,orfair,toexpecttheunderlyingsystemtoresolvedeadlocksautomatically.Asforthedeveloper,welookatthedesignissues
relatedtodeadlockpreventionandevendevelopatoolthatcanbeusedtodetectadeadlockinChapter6.
ThetechniqueusedtofixtheprobleminChapter6istomakesurethattheresetScore()methodacquiresthelocksinthesameorderasthe
newCharacter()method:
packagejavathreads.examples.ch03.example9;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
publicvoidresetScore(){
try{
scoreLock.lock();
charLock.lock();
score=0;
char2type=1;
setScore();
}finally{
charLock.unlock();
scoreLock.unlock();
}
}
}
Notethattheorderinwhichwereleasethelocksisimmaterial.
Lock Fairness
Thelastquestionweneedtoaddressisthequestionoflockfairness.Whatifwewantlockstobeissuedinafairfashion?Whatdoesitmeantobefair?The
ReentrantLockclassallowsthedevelopertorequestthatlocksbegrantedfairly.Thisjustmeansthatlocksaregrantedinasclosetoarrivalorderas
possible.Whilethisisfairforthemajorityofprograms,thedefinitionof"fair"canbemuchmorecomplex.
Whetherlocksaregrantedfairlyissubjective(i.e.,itismeasuredbytheuser'sperceptionsorotherrelativemeans)andcanbedependentonparticularneeds
oftheprogram.Thismeansthatfairnessisbasedonthealgorithmoftheprogramandonlyminimallybasedonthesynchronizationconstructthatthe
programuses.Inotherwords,achievingtotalfairnessisdependentontheneedsoftheprogram.Thebestthatthethreadinglibrarycanaccomplishistogrant
locksinafashionthatisspecifiedandconsistent.
Howshouldlocksbegrantedwithexplicitlocks?Onepossibilityisthatlocksshouldbegrantedonafirstcomefirstservedbasis.Anotheristheyshouldbe
grantedinanorderthatpermitsservicingthemaximumnumberofrequests.Forexample,ifwehavemultiplerequeststomakeawithdrawalfromabank
account,perhapsthesmallerwithdrawalrequestsshouldbeacceptedfirstorperhapsdepositsshouldhavepriorityoverwithdrawals.Athirdviewisthat
locksshouldbegrantedinafashionthatisbestfortheplatformregardlessofwhetheritisforabankingapplication,agolfingapplication,orourtyping
application.
Thebehaviorofsynchronization(usingthesynchronizedkeywordorexplicitlocks)isclosesttothelastview.Javasynchronizationprimitivesarenot
designedtograntlocksforaparticularsituationtheyarepartofageneralpurposethreadslibrary.So,thereisnoreasonthatthelocksshouldbegranted
basedonarrivalorder.Locksaregrantedbasedonimplementationspecificbehavioroftheunderlyingthreadingsystem,butitispossibletobasethelock
acquisitionsoftheReentrantLockclassonarrivalorder.
Let'sexamineaslightvariationtoourexamples.Typically,we'vedeclaredthelockasfollows:
privateLockscoreLock=newReentrantLock();
Wecandeclarethelocklikethisinstead:
privateLockscoreLock=newReentrantLock(true);
TheReentrantLockclassprovidesanoptioninitsconstructortospecifywhethertoissuelocksina"fair"fashion.Inthiscase,thedefinitionof"fair"is
firstinfirstout.Thismeansthatwhenmanylockrequestsaremadeatthesametime,theyaregrantedveryclosetotheorderinwhichtheyaremade.Ata
minimum,thispreventslockstarvationfromoccurring.
Thischangeisnotactuallyneededforourexample.Wehaveonlytwothreadsthataccessthislock.Onethreadisexecutedonlyonceeverysecondorso
whiletheotherthreadisdependentontheusertypingcharacters.Sincetheoperationofbothmethodsisshort,thechancesofanythreadwaitingforalockis
smallandthechancesoflockstarvationiszero.Itisuptothedevelopertodecidewhetherornottousethisoptiontheneedtoprovideaconsistentorderin
grantinglocksmustbebalancedwiththeoverheadoftheextracoderequiredtousethisoption.
Whatifyourprogramhasadifferentnotionoffairness?Inthatcase,it'suptoyoutodevelopalockingclassthatmeetstheneedsofyourapplication.Sucha
classneedsmorefeaturesofthethreadinglibrarythanwe'vediscussedsofaragoodmodelfortheclasswouldbetheReentrantReadWriteLock
examinedinChapter6.
Summary
Inthischapter,we'veintroducedthesynchronizedkeywordoftheJavalanguage.Thiskeywordallowsustosynchronizemethodsandblocksofcode.
We'vealsoexaminedthebasicsynchronizationclassesprovidedbytheJavaclasslibrarytheReentrantLockclassandtheLockinterface.These
classesallowustolockobjectsacrossmethodsandtoacquireandreleasethelockatwillbasedonexternalevents.Theyalsoprovidefeaturessuchastesting
toseeifthelockisavailable,placingtimeoutsonobtainingthelock,orcontrollingtheorderongrantinglocks.
We'vealsolookedatacommonwayofhandlingsynchronizationofasinglevariable:thevolatilekeyword.Usingthevolatilekeywordistypically
easierthansettingupneededsynchronizationaroundasinglevariable.
Thisconcludesourfirstlookatsynchronization.Asyoucantell,itisoneofthemostimportantaspectsofthreadedprogramming.Withoutthesetechniques,
wewouldnotbeabletosharedatacorrectlybetweenthethreadsthatwecreate.However,we'vejustbeguntolookathowthreadscansharedata.Thesimple
synchronizationtechniquesofthischapterareagoodstartinthenextchapter,welookathowthreadscannotifyeachotherthatdatahasbeenchanged.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
SwingTypeTesterwithScoreLabel
javathreads.examples.ch03.example1.SwingTypeTester
ch3ex1
ScoreLabelwithexplicitlock
javathreads.examples.ch03.example2.SwingTypeTester
ch3ex2
ScoreLabelwithexplicitlockingatasmallscope
javathreads.examples.ch03.example3.SwingTypeTester
ch3ex3
ScoreLabelwithsynchronizedblocklocking
javathreads.examples.ch03.example4.SwingTypeTester
ch3ex4
ScoreLabelwithnestedlocks
javathreads.examples.ch03.example5.SwingTypeTester
ch3ex5
DeadlockingAnimationCanvas
javathreads.examples.ch03.example6.SwingTypeTester
ch3ex6
DeadlockingAnimationCanvas(scopecorrected)
javathreads.examples.ch03.example7.SwingTypeTester
ch3ex7
DeadlockingScoreLabel
javathreads.examples.ch03.example8.SwingTypeTester
ch3ex8
DeadlockingScoreLabel(deadlockcorrected)
javathreads.examples.ch03.example9.SwingTypeTester
ch3ex9
Chapter4.Thread Notification
Inthepreviouschapter,wediscusseddatasynchronization.Usingsynchronizationandexplicitlocks,threadscaninteroperateandsafelysharedatawithout
anyraceconditionsthatmightcorruptthestateofthedata.However,asweshallsee,synchronizationismorethanavoidingraceconditions:itincludesa
threadbasednotificationsystemthatweexamineinthischapter.
Threadnotificationaddressesanumberofissuesinoursampleapplication.Twooftheserelatetotherandomcharactergeneratorandtheanimationcanvas.
TherandomcharactergeneratoriscreatedwhentheuserpressestheStartbuttonitisdestroyedwhentheuserpressestheStopbutton.Therefore,the
listenerstotherandomcharactergeneratorarereconnectedeachtimetheStartbuttonispressed.Infact,theentireinitializationprocessisrepeatedeverytime
thattheStartbuttonispressed.
Asimilarproblemexistsfortheanimationcomponent.Althoughthecomponentitselfisnotdestroyedeverytimetheuserrestarts,thethreadobjectthatis
usedfortheanimationisdiscardedandrecreated.Thecomponentprovidesamechanismthatallowsthedevelopertosetthedoneflag,butthecomponent
doesn'tusethatdatatorestarttheanimation:oncethedoneflagissettotrue,therun()methodoftheanimationcanvasexits.Thereasonforthishasto
dowithefficiency.Thealternativeistoloopforever,waitingforthedoneflagtobesettofalse.ThisconsumesalotofCPUcycles.Fortunately,the
mechanismsweexploreinthischaptercansolvealltheseproblems.
communicationbetweenthreads.
Theideabehindthemechanismissimple:onethreadneedsacertainconditiontoexistandassumesthatanotherthread
willcreatethatcondition.Whenanotherthreadcreatesthecondition,itnotifiesthefirstthreadthathasbeenwaitingforthecondition.Thisisaccomplished
withthefollowingmethodsoftheObjectclass:
voidwait()
Waitsforaconditiontooccur.Thismethodmustbecalledfromwithinasynchronizedmethodorblock.
voidwait(longtimeout)
Waitsforaconditiontooccur.However,ifthenotificationhasnotoccurredintimeoutmilliseconds,itreturnsanyway.Thismethodmustbecalled
fromasynchronizedmethodorblock.
voidwait(longtimeout,intnanos)
Waitsforaconditiontooccur.However,ifthenotificationhasnotoccurredintimeoutmillisecondsandnanosnanoseconds,itreturnsanyway.This
methodmustbecalledfromasynchronizedmethodorblock.Notethat,justlikethesleep()method,implementationsofthismethoddonotactually
supportnanosecondresolution.
voidnotify()
Notifiesathreadthatiswaitingthattheconditionhasoccurred.Thismethodmustbecalledfromwithinasynchronizedmethodorblock.
Whatisthepurposeofthewaitandnotifymechanism,andhowdoesitwork?Thewaitandnotifymechanismisasynchronizationmechanism.However,it
ismoreofacommunicationmechanism:itallowsonethreadtocommunicatetoanotherthreadthataparticularconditionhasoccurred.Thewaitandnotify
mechanismdoesnotspecifywhatthespecificconditionis.
Canthewaitandnotifymechanismbeusedtoreplacethesynchronizedmechanism?Actually,theanswerisnowaitandnotifydoesnotsolvetherace
conditionproblemthatthesynchronizedmechanismsolves.Asamatteroffact,waitandnotifymustbeusedinconjunctionwiththesynchronizedlockto
preventaraceconditioninthewaitandnotifymechanismitself.
Let'susethistechniquetosolvetheefficiencyprobleminouranimationcomponent.Inthisfixedversion,theanimationthreaddoesnotexitwhenthedone
flagisset.Instead,itsimplywaitsforthedoneflagtobereset.
packagejavathreads.examples.ch04.example1;
...
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
privatebooleandone=true;
...
publicsynchronizedvoidrun(){
while(true){
try{
if(done){
wait();
}else{
repaint();
wait(100);
}
}catch(InterruptedExceptionie){
return;
}
}
}
publicsynchronizedvoidsetDone(booleanb){
done=b;
if(timer==null){
timer=newThread(this);
timer.start();
}
if(!done)
notify();
}
}
Inthisnewversion,thedoneflagisnolongervolatile.Thisisbecausewearedoingmorethanjustsettingtheflagwealsoneedtosendanotification
atomicallywhilesettingtheflag.Therefore,accesstothedoneflagisnowprotectedbyasynchronizedlock.
Therun()methodnownolongerexitswhenthedoneflagissettofalse.Instead,itcallsthewait()method(withnoarguments).Thethreadwaits(or
blocks)inthatmethoduntilanotherthreadcallsthenotifymethod,atwhichpointitrestartstheanimation.
Alsonoticethatinsteadofcallingthesleep()method,theanimationisachievedbycallingthewait()methodwitha100millisecondtimeout.Thisis
duetothedifferencesbetweenthewait()andsleep()methods.Unlikethesleep()method,thewait()methodrequiresthatthethreadownthe
synchronizationlockoftheobject.Whenthewait()methodexecutes,thesynchronizationlockisreleased(internallybythevirtualmachineitself).Upon
receivingthenotification,thethreadneedstoreacquirethesynchronizationlockbeforereturningfromthewait()method.
Thistechniqueisneededduetoaraceconditionthatwouldotherwiseexistbetweensettingandsendingthenotificationandtestingandgettingthenotification.
Ifthewait()andnotify()mechanismwerenotinvokedwhileholdingthesynchronizationlock,therewouldbenowaytoguaranteethatthenotification
wouldbereceived.Andifthewait()methoddidnotreleasethelockpriortowaiting,itwouldbeimpossibleforthenotify()methodtobecalled(asit
wouldbeunabletoobtainthelock).Thisisalsowhywehadtousethewait()methodinsteadofthesleep()methodifthesleep()methodwere
used,thelockwouldneverbereleased,thesetDone()methodwouldneverrun,andnotificationcouldneverbesent.
Intheonlineexamples,therandomcharactergenerator'srestartingissuehasalsobeenfixed.We'llleaveituptoyoutoexaminethecodeatyourleisure.
somethreadbeexecutingthewait()methodwhenanotherthreadcallsthenotify()method.Sincethewaitandnotifymechanismdoesnotknowthe
conditionaboutwhichitissendingnotification,itassumesthatanotificationgoesunheardifnothreadiswaiting.Inotherwords,ifthenotify()methodis
calledwhennootherthreadiswaiting,notify()simplyreturnsandthenotificationislost.Athreadthatlaterexecutesthewait()methodhastowaitfor
anothernotificationtooccur.
Whatarethedetailsoftheraceconditionthatexistsinthewaitandnotifymechanism?Ingeneral,athreadthatusesthewait()methodconfirmsthata
conditiondoesnotexist(typicallybycheckingavariable)andthencallsthewait()method.Whenanotherthreadestablishesthecondition(typicallyby
settingthesamevariable),itcallsthenotify()method.Araceconditionoccurswhen:
1.Thefirstthreadteststheconditionandconfirmsthatitmustwait.
2.Thesecondthreadsetsthecondition.
3.Thesecondthreadcallsthenotify()methodthisgoesunheardsincethefirstthreadisnotyetwaiting.
4.Thefirstthreadcallsthewait()method.
Howdoesthispotentialraceconditiongetresolved?Thisraceconditionisresolvedbythesynchronizationlockdiscussedearlier.Inordertocallthewait()
ornotify()methods,wemusthaveobtainedthelockfortheobjectonwhichwe'recallingthemethod.Thisismandatorythemethodsdonotwork
properlyandgenerateanexceptionconditionifthelockisnotheld.Furthermore,thewait()methodalsoreleasesthelockpriortowaitingandreacquires
thelockpriortoreturningfromthewait()method.Thedevelopermustusethislocktoensurethatcheckingtheconditionandsettingtheconditionis
atomic,whichtypicallymeansthatthecheckorsetmustbewithinthelockscope.
Istherearaceconditionduringtheperiodthatthewait()methodreleasesandreacquiresthelock?Thewait()methodistightlyintegratedwiththelock
mechanism.Theobjectlockisnotactuallyfreeduntilthewaitingthreadisalreadyinastateinwhichitcanreceivenotifications.Thiswouldhavebeen
difficult,ifnotimpossible,toaccomplishifwehadneededtoimplementthewait()andnotify()methodsourselves.Thesystempreventsanyrace
conditionsfromoccurringinthismechanism.
Ifathreadreceivesanotification,isitguaranteedthattheconditionissetcorrectly?Simply,no.Priortocallingthewait()method,athreadshouldalways
testtheconditionwhileholdingthesynchronizationlock.Uponreturningfromthewait()method,thethreadshouldalwaysretesttheconditionto
determineifitshouldwaitagain.Thisisbecauseanotherthreadcanalsotesttheconditionanddeterminethatawaitisnotnecessaryprocessingthevalid
datathatwassetbythenotificationthread.
Let'slookintohowthatcanhappen.Ouranimatedcanvasexampleisverysimpleonlyonethreadisactuallywaiting.Inmostprograms,manythreadsare
waitingandsendingnotifications.Araceconditionexistswhenmultiplethreadsarewaitingfornotification.Theraceconditionthatissolvedinternallytothe
waitandnotifymechanismpreventsthelossofnotifications,butitdoesnotsolvethefollowingscenariowhenmultiplethreadsarewaiting:
1.Thread1callsamethodthatacquiresthesynchronizationlock.
2.Thread1examinesastateflaganddeterminesthatthedataisnotinthedesiredstate.
3.Thread1callsthewait()method,whichfreesthelock.
4.Thread2callsamethodthatacquiresthesamesynchronizationlock.
5.Thread3callsamethodthatblockswaitingforthelock.
6.Thread2setsthestateflagandcallsthenotify()method.
7.Thread2finishesitsmethodandfreesthelock.
8.Thread3acquiresthelockandproceedstoprocessthedataitseesthatthedataisinthedesiredstate,soitprocessesthedataandresetsthestateflag.
9.Thread3exitswithoutneedingtowait.
10.Thread1receivesthenotificationandwakesup.
Thisisacommoncasewhenmultiplethreadsareinvolvedinthenotifications.Moreparticularly,thethreadsthatareprocessingthedatacanbethoughtofas
consumerstheyconsumethedataproducedbyotherthreads.Thereisnoguaranteethatwhenaconsumerreceivesanotificationthatithasnotbeen
processedbyanotherconsumer.Assuch,whenaconsumerwakesup,itcannotassumethatthestateitwaswaitingforisstillvalid.Itmayhavebeenvalidin
thepast,butthestatemayhavebeenchangedafterthenotify()methodwascalledandbeforetheconsumerthreadwokeup.Waitingthreadsmustprovide
theoptiontocheckthestateandtoreturnbacktoawaitingstateincasethenotificationhasalreadybeenhandled.Thisiswhywealwaysputcallstothe
wait()methodinaloop.
Remembertoothatthewait()methodcanreturnearlyifitsthreadisinterrupted.Inthatcase,processingisapplicationspecific,dependingonhowthe
algorithmneedstohandletheinterruption.
voidnotifyAll()
Notifiesallthethreadswaitingontheobjectthattheconditionhasoccurred.Thismethodmustbecalledfromwithinasynchronizedmethodorblock.
ThenotifyAll()methodissimilartothenotify()methodexceptthatallofthethreadsthatarewaitingontheobjectarenotifiedinsteadofasingle
arbitrarythread.Justlikethenotify()method,thenotifyAll()methoddoesnotallowustodecidewhichthreadgetsthenotification:theyallget
notified.Whenallthethreadsreceivethenotification,itispossibletoworkoutamechanismforthethreadstochooseamongthemselveswhichthreadshould
continueandwhichthread(s)shouldcallthewait()methodagain.
DoesthenotifyAll()methodreallywakeupallthethreads?Yesandno.Allofthewaitingthreadswakeup,buttheystillhavetoreacquiretheobject
lock.Sothethreadsdonotruninparallel:theymusteachwaitfortheobjectlocktobefreed.Thus,onlyonethreadcanrunatatime,andonlyafterthethread
thatcalledthenotifyAll()methodreleasesitslock.
Whywouldyouwanttowakeupallofthethreads?Thereareafewreasons.Forexample,theremightbemorethanoneconditiontowaitfor.Sincewe
cannotcontrolwhichthreadgetsthenotification,itisentirelypossiblethatanotificationwakesupathreadthatiswaitingforanentirelydifferentcondition.
[ 2 ]
Bywakingupallthethreads,wecandesigntheprogramsothatthethreadsdecideamongthemselveswhichthreadshouldexecutenext.
Anotheroptioncouldbewhenproducersgeneratedatathatcansatisfymorethanoneconsumer.Sinceitmaybedifficulttodeterminehowmanyconsumers
canbesatisfiedwiththenotification,anoptionistonotifythemall,allowingtheconsumerstosortitoutamongthemselves.
publicsynchronizedvoidnewCharacter(CharacterEventce){
...
}
protectedsynchronizedvoidpaintComponent(Graphicsgc){
...
}
publicvoidrun(){
synchronized(doneLock){
while(true){
try{
if(done){
doneLock.wait();
}else{
repaint();
doneLock.wait(100);
}
}catch(InterruptedExceptionie){
return;
}
}
}
}
publicvoidsetDone(booleanb){
synchronized(doneLock){
done=b;
if(timer==null){
timer=newThread(this);
timer.start();
}
if(!done)
doneLock.notify();
}
}
}
Inthisexample,we'veseparatedthesynchronizationthatprotectstheanimation(thetmpChar[]andcurXvariables)fromthesynchronizationthatprotects
thethreadstate(thetimeranddonevariables).Inprogramswithalotofcontentionforobjectlocks,thistechniqueisusefulsinceitallowsmorethreadsto
accessdifferentmethodsatthesametime(e.g.,twothreadscannowsimultaneouslyaccessthepaintComponent()andrun()methods).
Nowwhenthewait()andnotify()methodsarecalled,we'reholdingtheobjectlockofthedoneLockobject.Consequently,weexplicitlycallthe
doneLock.wait()anddoneLock.notify()methods.Thatfollowsthesamelogicweoutlinedearlierit'ssimplyadifferentlocknow.
ItmayhelptoremindyourselfhowJavaobjectsworkinthisregard.Inourfirstexample,wehadthisstatement:
wait();
whichisequivalenttothisstatement:
this.wait();
Sothewait()andnotify()methodsareconsistent:theyarealwayscalledwithanobjectreference,evenifthatreferenceistheimpliedthisobject.The
objectreferencemustalwaysbeonethatyouholdtheobjectlockforandagain,thesynchronizedmethodgrabstheobjectlockofthethisobject.
Condition Variables
Conditionvariablesareatypeofsynchronizationprovidedbymanyotherthreadingsystems.AconditionvariableisverysimilartoJava'swaitandnotify
mechanisminfact,inmostcasesitisfunctionallyidentical.ThefourbasicfunctionsofaPOSIXconditionvariablewait(),timed_wait(),
signal(),andbroadcast()mapdirectlytothemethodsprovidedbyJava(wait(),wait(long),notify(),andnotifyAll(),respectively).
Theimplementationsarealsologicallyidentical.Thewait()operationofaconditionvariablerequiresthatamutexlockbeheld.Itreleasesthelockwhile
waitingandreacquiresthelockpriortoreturningtothecaller.Thesignal()functionwakesuponethreadwhereasthebroadcast()functionwakesup
allthewaitingthreads.Thesefunctionsalsorequirethatthemutexbeheldduringthecall.Theraceconditionsofaconditionvariablearesolvedinthesame
wayasthoseofJava'swaitandnotifymechanism.
Thereisonesubtledifference,however.Thewaitandnotifymechanismishighlyintegratedwithitsassociatedlock.Thismakesthemechanismeasierto
usethanitsconditionvariablecounterpart.Callingthewait()andnotify()methodsfromsynchronizedsectionsofcodeisjustanaturalpartoftheiruse.
Usingconditionvariables,however,requiresthatyoucreateaseparatemutexlock,storethatmutex,andeventuallydestroythemutexwhenitisnolonger
necessary.
Unfortunately,thatconveniencecomesatasmallprice.APOSIXconditionvariableanditsassociatedmutexlockareseparatesynchronizationentities.Itis
possibletousethesamemutexwithtwodifferentconditionvariables,oreventomixandmatchmutexesandconditionvariablesinanyscope.Whilethe
waitandnotifymechanismismucheasiertouseandisusableformostcasesofsignalbasedsynchronization,itisnotcapableofassigningany
synchronizationlocktoanynotificationobject.Whenyouneedtosignaltwodifferentnotificationobjectswhilerequiringthesamesynchronizationlockto
protectcommondata,aconditionvariableismoreefficient.
J2SE5.0addsaclassthatprovidesthefunctionalityofconditionvariables.ThisclassisusedinconjunctionwiththeLockinterface.Sincethisnewinterface
(and,therefore,object)isseparatefromthecallingobjectandthelockobject,itsusageisjustasflexibleastheconditionvariablesinotherthreadingsystems.
InJava,conditionvariablesareobjectsthatimplementtheConditioninterface.TheConditioninterfaceistiedtotheLockinterface,justasthewaitand
notifymechanismistiedtothesynchronizationlock.
TocreateaConditionobjectfromtheLockobject,youcallamethodavailableontheLockobject:
Locklockvar=newReentrantLock();
Conditioncondvar=lockvar.newCondition();
UsingtheConditionobjectissimilartousingthewaitandnotifymechanism,withtheConditionobject'sawait()andsignal()methodcalls
replacingthewait()andnotify()methods.We'llmodifyourtypingprogramtousetheconditionvariableinsteadofthewaitandnotifymethods.This
time,we'llshowtheimplementationoftherandomcharactergeneratorthecodefortheanimationcharacterclassissimilarandcanbefoundonline.
packagejavathreads.examples.ch04.example3;
...
publicclassRandomCharacterGeneratorextendsThreadimplementsCharacterSource{
...
privateLocklock=newReentrantLock();
privateConditioncv=lock.newCondition();
...
publicvoidrun(){
try{
lock.lock();
while(true){
try{
if(done){
cv.await();
}else{
nextCharacter();
cv.await(getPauseTime(),TimeUnit.MILLISECONDS);
}
}catch(InterruptedExceptionie){
return;
}
}
}finally{
lock.unlock();
}
}
publicvoidsetDone(booleanb){
try{
lock.lock();
done=b;
if(!done)cv.signal();
}finally{
lock.unlock();
}
}
}
Aswementioned,anewConditionobjectiscreatedbycallingthenewCondition()methodprovidedbytheLockinterface.ThisnewCondition
objectisboundtotheLockinstancewhosemethodiscalled.ThismeansthatthelockoftheLockinstancemustbeheldinordertousetheCondition
objectitalsomeansthattheConditionobjectreleasesandreacquiresthelocksimilartothewayJava'swaitandnotifymechanismworkswith
synchronizationlocks.
Therefore,ournewrandomcharactergeneratornowusesaLockobjectasitssynchronizationlock.WeinstantiateaConditionobject,cv,whichissetto
thevaluereturnedbythenewCondition()methodofthelockobject.Furthermore,callstothewait()andnotify()methodarereplacedbythe
conditionobject'sawait()andsignal()method.
Inthisexample,itdoesn'tlooklikeweaccomplishedanything:allwedoisusedifferentmethodstoaccomplishwhatwewerepreviouslyabletoaccomplish
usingthewaitandnotifymechanism.Ingeneral,conditionvariablesarenecessaryforseveralreasons.
First,conditionvariablesareneededwhenyouuseLockobjects.Usingthewait()andnotify()methodsoftheLockobjectwillnotworksincethese
methodsarealreadyusedinternallytoimplementtheLockobject.Moreimportantly,justbecauseyouholdtheLockobjectdoesn'tmeanyouholdthe
synchronizationlockofthatobject.Inotherwords,thelockrepresentedbytheLockobjectandthesynchronizationlockassociatedwiththeobjectare
distinct.WeneedaconditionvariablemechanismthatunderstandsthelockingmechanismprovidedbytheLockobject.Thisconditionvariablemechanism
isprovidedbytheConditionobject.
ThesecondreasonisthecreationoftheConditionobject.UnliketheJavawaitandnotifymechanism,Conditionobjectsarecreatedasseparateobjects.
ItispossibletocreatemorethanoneConditionobjectperlockobject.Thatmeanswecantargetindividualthreadsorgroupsofthreadsindependently.
WiththestandardJavamechanism,allwaitingthreadsthataresynchronizingonthesameobjectarealsowaitingonthesamecondition.
HereareallthemethodsoftheConditioninterface.ThesemethodsmustbecalledwhileholdingthelockoftheobjecttowhichtheConditionobjectis
tied:
voidawait()
Waitsforaconditiontooccur.
voidawaitUninterruptibly()
Waitsforaconditiontooccur.Unliketheawait()method,itisnotpossibletointerruptthiscall.
longawaitNanos(longnanosTimeout)
Waitsforaconditiontooccur.However,ifthenotificationhasnotoccurredinnanosTimeoutnanoseconds,itreturnsanyway.Thereturnvalueisan
estimateofthetimeoutremainingareturnvalueequalorlessthanzeroindicatesthatthemethodisreturningduetothetimeout.Asusual,theactual
resolutionofthismethodisplatformspecificandusuallytakesmillisecondsinpractice.
booleanawait(longtime,TimeUnitunit)
Waitsforaconditiontooccur.However,ifthenotificationhasnotoccurredinthetimeoutspecifiedbythetimeandunitpair,itreturnswithavalueof
false.
booleanawaitUntil(Datedeadline)
Waitsforaconditiontooccur.However,ifthenotificationhasnotoccurredbytheabsolutetimespecified,itreturnswithavalueoffalse.
voidsignal()
NotifiesathreadthatiswaitingusingtheConditionobjectthattheconditionhasoccurred.
voidsignalAll()
NotifiesallthethreadswaitingusingtheConditionobjectthattheconditionhasoccurred.
Basically,themethodsoftheConditioninterfaceduplicatethefunctionalityofthewaitandnotifymechanism.Afewconveniencemethodsallowthe
developertoavoidbeinginterruptedortospecifyatimeoutbasedonrelativeorabsolutetimes.
Summary
Inthischapter,weintroducedthemethodsofthewaitandnotifymechanism.WealsoexaminedtheConditioninterface,whichprovidesanotification
counterpartfortheLockinterface.
WiththesemethodsoftheObjectclassandConditioninterface,threadsareabletointeroperateefficiently.Insteadofjustprovidingprotectionagainst
raceconditions,wenowhavewaysforthreadstoinformeachotherabouteventsorconditionswithoutresortingtopollingandtimeouts.
Inlaterchapters,weexamineclassesandtechniquesthatprovideevenhigherlevelsupportfordatasynchronizationandthreadcommunication.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Ant
target
SwingTypeTesterwithwaitandnotifymechanism
javathreads.examples.ch04.example1.SwingTypeTester
ch4
ex1
SwingTypeTesterwithwaitandnotifymechanisminsynchronized
javathreads.examples.ch04.example2.SwingTypeTester
blocks
SwingTypeTesterwithconditionvariables
ch4
ex2
javathreads.examples.ch04.example3.SwingTypeTester
ch4
ex3
[ 1 ]
[ 2 ]
WithSolarisorPOSIXthreads,theseareoftenreferredtoasconditionvariableswithWindows,theyarereferredtoaseventvariables.
Laterinthischapter,wediscussoptionstoallowmultipleconditionvariablestocoexist.Thisallowsdifferentthreadstowaitfor
differentconditionsefficiently.
Inpracticalterms,thesecondpointhereisthemostsalient:ifsomeoneelseholdsthelock,youhavetowaitforit,whichcangreatlydecreasetheperformance
ofyourprogram.WediscusstheperformanceofthreadrelatedoperationsinChapter14.
Thissituationleadsprogrammerstoattempttolimitsynchronizationintheirprograms.Thisisagoodideayoucertainlydon'twanttohaveunneeded
synchronizationinyourprogramanymorethanyouwanttohaveunneededcalculations.Butaretheretimeswhenyoucanavoidsynchronizationaltogether?
We'vealreadyseenthatinonecasetheanswerisyes:youcanusethevolatilekeywordforaninstancevariable(otherthanadoubleorlong).Those
variablescannotbepartiallystored,sowhenyoureadthem,youknowthatyou'rereadingavalidvalue:thelastvaluethatwasstoredintothevariable.Later
inthischapter,we'llseeanothercasewhereallowingunsychronizedaccesstodataisacceptablebycertainclasses.
Butthesearereallytheonlycasesinwhichyoucanavoidsynchronization.Inallothercases,ifmultiplethreadsaccessthesamesetofdata,youmust
explicitlysynchronizeallaccesstothatdatainordertopreventvariousraceconditions.
Thereasonsforthishavetodowiththewayinwhichcomputersoptimizeprograms.Computersperformtwoprimaryoptimizations:creatingregistersto
holddataandreorderingstatements.
Yourcomputerhasacertainamountofmainmemoryinwhichitstoresthedataassociatedwithyourprogram.Whenyoudeclareavariable(suchasthe
doneflagusedinseveralofourclasses),thecomputersetsasideaparticularmemorylocationthatholdsthevalueofthatvariable.
MostCPUsareabletooperatedirectlyonthedatathat'sheldinmainmemory.OtherCPUscanonlyreadandwritetomainmemorylocationsthese
computersmustreadthedatafrommainmemoryintoaregister,operateonthatregister,andthenstorethedatatomainmemory.YetevenCPUsthatcan
operateondatadirectlyinmainmemoryusuallyhaveasetofregistersthatcanholddata,andoperatingonthedataintheregisterisusuallymuchfasterthan
operatingonthedatainmainmemory.Consequently,registeruseispervasivewhenthecomputerexecutesyourcode.
Fromalogicalperspective,everythreadhasitsownsetofregisters.WhentheoperatingsystemassignsaparticularthreadtoaCPU,itloadstheCPU
registerswithinformationspecifictothatthreaditsavestheregisterinformationbeforeitassignsadifferentthreadtotheCPU.So,threadsneversharedata
thatisheldinregisters.
Let'sseehowthisappliestoaJavaprogram.Whenwewanttoterminateathread,wetypicallyuseadoneflag.Thethread(orrunnableobject)contains
code,suchas:
publicvoidrun(){
while(!done){
foo();
}
}
publicvoidsetDone(){
done=true;
}
Supposewedeclaredoneas:
privatebooleandone=false;
Thisassociatesaparticularmemorylocation(e.g.,0xff12345)withthevariabledoneandsetsthevalueofthatmemorylocationto0(themachine
representationofthevaluefalse).
Therun()methodisthencompiledintoasetofinstructions:
Beginmethodrun
Loadregisterr1withmemorylocation0Xff12345
LabelL1:
Testifregisterr1==1
IftruebranchtoL2
Callmethodfoo
BranchtoL1
LabelL2:
Endmethodrun
Meanwhile,thesetDone()methodlookssomethinglikethis:
BeginmethodsetDone
Store1intomemorylocation0xff12345
EndmethodsetDone
Youcanseetheproblem:therun()methodneverreloadsregisterr1withthecontentsofmemorylocation0xff12345.Therefore,therun()methodnever
terminates.
However,supposewedefinedoneas:
privatevolatilebooleandone=false;
Nowtherun()methodlogicallylookslikethis:
Beginmethodrun
LabelL1:
Testifmemorylocation0xff12345==1
IftruebranchtoL2
Callmethodfoo
BranchtoL1
LabelL2:
Endmethod
[ 1 ]
Usingthevolatilekeywordensuresthatthevariableisneverkeptinaregister.Thisguaranteesthatthevariableistrulysharedbetweenthreads.
Rememberthatwemighthaveimplementedthiscodebysynchronizingaroundaccesstothedoneflag(ratherthanmakingthedoneflagvolatile).This
worksbecausesynchronizationboundariessignaltothevirtualmachinethatitmustinvalidateitsregisters.Whenthevirtualmachineentersasynchronized
methodorblock,itmustreloaddataithascachedinitslocalregisters.Beforethevirtualmachineexitsasynchronizationmethodorblock,itmuststoreitsl
ocalregisterstomainmemory.
totalscoreamonganumberofrunsofourtypinggame.WemightthenwritetheresetScore()methodlikethis:
publicintcurrentScore,totalScore,finalScore
publicvoidresetScore(booleandone){
totalScore+=currentScore;
if(done){
finalScore=totalScore;
currentScore=0;
}
}
publicintgetFinalScore(){
if(currentScore==0)
returnfinalScore;
return1;
}
Araceconditionexistsbecausewecanhavethisorderofexecutionbythreadst1andt2:
Thread1:Updatetotalscore
Thread2:SeeifcurrentScore==0
Thread2:Return1
Thread1:UpdatefinalScore
Thread1:SetcurrentScore=0
That'snotnecessarilyfataltoourprogramlogic.Ifwe'reperiodicallycheckingthescore,we'llget1thistime,butwe'llgetthecorrectanswernexttime.
Dependingonourprogram,thatmaybeperfectlyacceptable.
However,youcannotdependontheorderedexecutionofstatementslikethis.Thevirtualmachinemaydecidethatit'smoreefficienttostore0in
currentScorebeforeitassignsthefinalscore.Thisdecisionismadeatruntimebasedontheparticularhardwarerunningtheprogram.Inthatcase,we're
leftwiththissequence:
Thread1:Updatetotalscore
Thread1:SetcurrentScore=0
Thread2:SeeifcurrentScore==0
Thread2:ReturnfinalScore
Thread1:UpdatefinalScore
Nowtheraceconditionhascausedaproblem:we'vereturnedthewrongfinalscore.Notethatitdoesn'tmakeanydifferencewhetherthevariablesaredefined
asvolatile:statementsthatincludevolatilevariablescanbereorderedjustlikeanyotherstatements.
Theonlythingthatcanhelpushereissynchronization.IftheresetScore()andgetFinalScore()methodsaresynchronized,itdoesn'tmatterwhether
thestatementswithinmethodsarereorderedsincethesynchronizationpreventsusfrominterleavingthethreadexecutionofthemethods.
Synchronizedblocksalsopreventthereorderingofstatements.Thevirtualmachinecannotmoveastatementfrominsideasynchronizedblocktooutsidea
synchronizedblock.Note,however,thattheconverseisnottrue:astatementbeforeasynchronizedblockmaybemovedintotheblock,andastatementafter
asynchronizedblockmaybemovedintotheblock.
Double-Checked Locking
Thisdesignpatterngainedafairamountofattentionwhenitwasfirstproposed,butithasbeenprettythoroughlydiscreditedbynow.Still,itpopsupevery
nowandthen,soherearethedetailsforthecurious.
Onecasewheredevelopersaretemptedtoavoidsynchronizationdealswithlazyinitialization.Inthisparadigm,anobjectcontainsareferencethatistime
consumingtoconstruct,sothedeveloperdelaysconstructionoftheobject:
Foofoo;
publicvoiduseFoo(){
if(foo==null){
synchronized(this){
if(foo==null)
foo=newFoo();
}
}
foo.invoke();
}
Thedeveloper'sgoalhereistopreventsynchronizationoncethefooobjecthasbeeninitialized.Unfortunately,thispatternisbrokenbecauseofthereasons
we'vejustexamined.Inparticular,thevalueforfoocanbestoredbeforetheconstructorforfooiscalledasecondthreadenteringtheuseFoo()method
wouldthencallfoo.invoke()beforetheconstructorforfoohascompleted.Iffooisavolatileprimitive(butnotavolatileobject),thiscanbemade
toworkifyoudon'tmindthecasewherefooisinitializedmorethanonce(andwheremultipleinitializationsoffooareguaranteedtoproducethesame
value).ThisuseofvolatilesemanticsisonlyvalidinJava5andlaterreleases.
FormoreinformationonthedoublecheckedlockingpatternaswellasanextensivetreatmentoftheJavamemorymodel,see
http://www.cs.umd.edu/~pugh/java/memoryModel/(http://www.cs.umd.edu/~pugh/java/memoryModel/).
Atomic Variables
Thepurposeofsynchronizationistopreventtheraceconditionsthatcancausedatatobefoundineitheraninconsistentorintermediatestate.Multiplethreads
arenotallowedtoraceduringthesectionsofcodethatareprotectedbysynchronization.Thisdoesnotmeanthattheoutcomeororderofexecutionofthe
threadsisdeterministic:threadsmayberacingpriortothesynchronizedsectionofcode.Andifthethreadsarewaitingonthesamesynchronizationlock,the
orderinwhichthethreadsexecutethesynchronizedcodeisdeterminedbytheorderinwhichthelockisgranted(which,ingeneral,isplatformspecificand
nondeterministic).
Thisisasubtlebutimportantpoint:notallraceconditionsshouldbeavoided.Onlytheraceconditionswithinthreadunsafesectionsofcodeareconsidereda
problem.Wecanfixtheprobleminoneoftwoways.Wecansynchronizethecodetopreventtheraceconditionfromoccurring,orwecandesignthecodeso
thatitisthreadsafewithouttheneedforsynchronization(orwithonlyminimalsynchronization).
Wearesurethatyouhavetriedbothtechniques.Inthesecondcase,itisamatterofshrinkingthesynchronizationscopetobeassmallaspossibleand
reorganizingcodesothatthreadsafesectionscanbemovedoutsideofthesynchronizedblock.Usingvolatilevariablesisanothercaseofthisifenoughcode
canbemovedoutsideofthesynchronizedsectionofcode,thereisnoneedforsynchronizationatall.
Thismeansthatthereisabalancebetweensynchronizationandvolatilevariables.Itisnotamatterofdecidingwhichoftwotechniquescanbeusedbasedon
thealgorithmoftheprogramitisactuallypossibletodesignprogramstousebothtechniques.Ofcourse,thebalanceisveryonesidedvolatilevariablescan
besafelyusedonlyforasingleloadorstoreoperationandcan'tbeappliedtolongordoublevariables.Theserestrictionsmaketheuseofvolatilevariables
uncommon.
J2SE5.0providesasetofatomicclassestohandlemorecomplexcases.Insteadofallowingasingleatomicoperation(likeloadorstore),theseatomic
classesallowmultipleoperationstobetreatedatomically.Thismaysoundlikeaninsignificantenhancement,butasimplecompareandsetoperationthatis
atomicmakesitpossibleforathreadto"grabaflag."Inturn,thismakesitpossibletoimplementalockingmechanism:infact,theReentrantLockclass
implementsmuchofitsfunctionalitywithonlyatomicclasses.Intheory,itispossibletoimplementeverythingwehavedonesofarwithoutJava
synchronizationatall.
Inthissection,weexaminetheseatomicclasses.Theatomicclasseshavetwouses.Theirfirst,andsimpler,useistoprovideclassesthatcanperformatomic
operationsonsinglepiecesofdata.Avolatileinteger,forexample,cannotbeusedwiththe++operatorbecausethe++operatorcontainsmultiple
instructions.TheAtomicIntegerclass,however,hasamethodthatallowstheintegeritholdstobeincrementedatomically(yetstillwithoutusing
synchronization).
Thesecond,andmorecomplex,useoftheatomicclassesistobuildcomplexcodethatrequiresnosynchronizationatall.Codethatneedstoaccesstwoor
moreatomicvariables(orperformtwoormoreoperationsonasingleatomicvariable)wouldnormallyneedtobesynchronizedinorderforbothoperations
tobeconsideredanatomicunit.However,usingthesamesortofcodingtechniquesastheatomicclassesthemselves,youcandesignalgorithmsthatperform
thesemultipleoperationsandstillavoidsynchronization.
complextypesisminimal.Forarrays,onlyoneindexedvariablecanbemodifiedatatimethereisnofunctionalitytomodifythewholearrayatomically.
AtomicarraysaremodelledusingtheAtomicIntegerArray,AtomicLongArray,andAtomicReferenceArrayclasses.Theseclassesbehaveas
arraysoftheirconstituentdatatype,butanarraysizemustbespecifiedduringconstructionandanindexmustbeprovidedduringoperation.Noclass
implementsanarrayofbooleans.Thisisonlyaminorinconvenience,assuchanarraycanbesimulatedusingtheAtomicIntegerArrayclass.
Volatilevariables(ofcertaintypes)thatarealreadydefinedinotherclassescanbeupdatedbyusingtheAtomicIntegerFieldUpdater,
AtomicLongFieldUpdater,andAtomicReferenceFieldUpdaterclasses.Theseclassesareabstract.Touseafieldupdater,youcallthestatic
newUpdater()methodoftheclass,passingittheclassandfieldnamesofthevolatileinstancevariablewithintheclassyouwishtoupdate.Youcanthen
performthesameatomicoperationsonthevolatilefield(e.g.,postincrementviathegetAndIncrement()method)asyoucanperformonotheratomic
variables.
Twoclassescompleteouroverviewoftheatomicclasses.TheAtomicMarkableReferenceclassandtheAtomicStampedReferenceclassallowa
markorstamptobeattachedtoanyobjectreference.Tobeexact,theAtomicMarkableReferenceclassprovidesadatastructurethatincludesanobject
referencebundledwithaboolean,andtheAtomicStampedReferenceclassprovidesadatastructurethatincludesanobjectreferencebundledwithan
integer.
Thebasicmethodsoftheseclassesareessentiallythesame,withslightmodificationstoallowforthetwovalues(thereferenceandthestampormark).The
get()methodnowrequiresanarraytobepassedasanargumentthestampormarkisstoredasthefirstelementofthearrayandthereferenceisreturned
asnormal.Othergetmethodsreturnjustthereference,mark,orstamp.Theset()andcompareAndSet()methodsrequireadditionalparameters
representingthemarkorstamp.Andfinally,theseclassescontainanattemptMark()orattemptStamp()method,usedtosetthemarkorstampbased
onanexpectedreference.
importjavax.swing.*;
importjava.awt.event.*;
importjava.util.concurrent.*;
importjava.util.concurrent.atomic.*;
importjavathreads.examples.ch05.*;
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
privateAtomicIntegerscore=newAtomicInteger(0);
privateAtomicIntegerchar2type=newAtomicInteger(1);
privateAtomicReference<CharacterSource>generator=null;
privateAtomicReference<CharacterSource>typist=null;
publicScoreLabel(CharacterSourcegenerator,CharacterSourcetypist){
this.generator=newAtomicReference(generator);
this.typist=newAtomicReference(typist);
if(generator!=null)
generator.addCharacterListener(this);
if(typist!=null)
typist.addCharacterListener(this);
}
publicScoreLabel(){
this(null,null);
}
publicvoidresetGenerator(CharacterSourcenewGenerator){
CharacterSourceoldGenerator;
if(newGenerator!=null)
newGenerator.addCharacterListener(this);
oldGenerator=generator.getAndSet(newGenerator);
if(oldGenerator!=null)
oldGenerator.removeCharacterListener(this);
}
publicvoidresetTypist(CharacterSourcenewTypist){
CharacterSourceoldTypist;
if(newTypist!=null)
newTypist.addCharacterListener(this);
oldTypist=typist.getAndSet(newTypist);
if(oldTypist!=null)
oldTypist.removeCharacterListener(this);
}
publicvoidresetScore(){
score.set(0);
char2type.set(1);
setScore();
}
privatevoidsetScore(){
//ThismethodwillbeexplainedinChapter7
SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
setText(Integer.toString(score.get()));
[ 2 ]
touseonlyatomicvariables:
}
});
}
publicvoidnewCharacter(CharacterEventce){
intoldChar2type;
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator.get()){
oldChar2type=char2type.getAndSet(ce.character);
if(oldChar2type!=1){
score.decrementAndGet();
setScore();
}
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
elseif(ce.source==typist.get()){
while(true){
oldChar2type=char2type.get();
if(oldChar2type!=ce.character){
score.decrementAndGet();
break;
}elseif(char2type.compareAndSet(oldChar2type,1)){
score.incrementAndGet();
break;
}
}
setScore();
}
}
}
Whenyoucomparethisclasstopreviousimplementations,you'llseethatwe'vemademorechangesherethansimplysubstitutingatomicvariablesfor
variablesthatwerepreviouslyprotectedbysynchronization.Removingthesynchronizationhasaffectedouralgorithmsindifferentways.We'vemadethree
kindsofmodifications:simplevariablesubstitution,changingalgorithms,andretryingoperations.
Thepointofeachmodificationistopreservethefullsemanticsofthesynchronizedversionoftheclass.Thesemanticsofsynchronizedcodearedependent
uponrealizingalltheeffectsofthecode.Itisn'tenoughtomakesurethatthevariablesusedbythecodeareupdatedatomically:youmustensurethattheend
effectofthecodeisthesameasthesynchronizedversion.We'lllookatthedifferentkindsofmodificationswemadetoseetheimplicationofthis
requirement.
VARIABLE SUBSTITUTION
Thesimplestkindofmodificationyoumayhavetomakeissimplysubstitutingatomicvariablesforthevariablesusedinapreviouslysynchronizedmethod.
That'swhathappensinournewimplementationoftheresetScore()method:Thescoreandchar2typevariableshavebeenchangedtoatomic
variables,andthismethodjustreinitializesthem.
Interestingly,changingbothvariablestogetherisnotdoneatomically:itispossibleforthescoretobechangedbeforethechangetothechar2typevariable
iscompleted.Thismaysoundlikeaproblem,butitactuallyisn'tbecausewe'vepreservedthesemanticsofthesynchronizedversionoftheclass.Our
previousimplementationsoftheScoreLabelclasshadasimilarraceconditionthatcouldcausethescoretobeslightlyoffiftheresetScore()method
iscalledwhilethelistenersarestillattachedtothesource.
Inpreviousimplementations,theresetScore()andnewCharacter()methodsaresynchronized,butthatonlymeanstheydonotrunsimultaneously.
ApendingcalltothenewCharacter()methodcanstillrunoutoforder(withrespecttotheresetScore()method)duetoarrivalorderorlock
acquisitionordering.SoatypisteventmaywaittobedelivereduntiltheresetScore()methodcompletes,butwhenitisdelivereditwillbeforanevent
thatisnowoutofdate.That'sthesameissuewe'llseewiththisimplementationoftheclass,wherechangingbothvariablesintheresetScore()methodis
nothandledatomically.
Rememberthatthepurposeofsynchronizationisnottopreventallraceconditionsitistopreventproblemraceconditions.Theraceconditionwiththis
implementationoftheresetScore()methodisnotconsideredaproblem.Inanycase,wecreateaversionofthistypinggamethatatomicallychanges
boththescoreandcharacterlaterinthischapter.
CHANGING ALGORITHMS
ThesecondtypeofchangeisembodiedwithinournewimplementationoftheresetGenerator()andresetTypist()methods.Ourearlierattemptat
havingaseparatesynchronizationlockfortheresetGenerator()andresetTypist()methodswasactuallyagoodidea.Neithermethodchangedthe
scoreorthechar2typevariables.Infact,theydon'tevenchangevariablesthataresharedwitheachotherthesynchronizationlockforthe
resetGenerator()methodisusedonlytoprotectthemethodfrombeingcalledsimultaneouslybymultiplethreads.Thisisalsotrueforthe
resetTypist()methodinfact,theissuesforbothmethodsarethesame,sowediscussonlytheresetGenerator()method.Unfortunately,making
thegeneratorvariableanAtomicReferencehasintroducedmultiplepotentialproblemsthatwe'vehadtoaddress.
TheseproblemsarisebecausethestateencapsulatedbytheresetGenerator()methodismorethanjustthevalueofthegeneratorvariable.Makingthe
generatorvariableanAtomicReferencemeansthatweknowoperationsonthatvariablewilloccuratomically.Butwhenweremovethesynchronization
fromtheresetGenerator()methodcompletely,wemustbesurethattheentirestateencapsulatedbythatmethodisstillconsistent.
Inthiscase,thestateincludestheregistrationoftheScoreLabelobject(thethisobject)withthecharactersourcegenerators.Afterthemethodcompletes,
wewanttoensurethatthethisobjectisregisteredwithonlyoneandonlyonegenerator(theoneassignedtothegeneratorinstancevariable).
ConsiderwhatwouldhappenwhentwothreadssimultaneouslycalltheresetGenerator()method.Inthisdiscussion,theexistinggeneratoris
generatorAonethreadiscallingtheresetGenerator()methodwithageneratorofgeneratorBandanotherthreadiscallingthemethodwitha
generatorcalledgeneratorC.
Ourpreviousexamplelookedlikethis:
if(generator!=null)
generator.removeCharacterListener(this);
generator=newGenerator;
if(newGenerator!=null)
newGenerator.addCharacterListener(this);
Inthiscode,thetwothreadssimultaneouslyaskgeneratorAtoremovethethisobject:ineffect,itwouldberemovedtwice.TheScoreLabelobject
wouldalsobeaddedtobothgeneratorBandgeneratorC.Bothofthoseeffectsareerrors.
Becauseourpreviousexamplewassynchronized,theseerrorswereprevented.Inourunsynchronizedcode,wemustdothis:
if(newGenerator!=null)
newGenerator.addCharacterListener(this);
oldGenerator=generator.getAndSet(newGenerator);
if(oldGenerator!=null)
oldGenerator.removeCharacterListener(this);
Theeffectsofthiscodemustbecarefullyconsidered.Whencalledbyourtwothreadssimultaneously,theScoreLabelobjectisregisteredwithboth
generatorBandgeneratorC.Thethreadsthensetthecurrentgeneratoratomically.Becausethey'reexecutingatthesametime,differentoutcomesare
possible.Supposethatthefirstthreadexecutesfirst:itgetsgeneratorAbackfromthegetAndSet()methodandthenremovestheScoreLabelobject
fromthelistenersofgeneratorA.ThesecondthreadgetsgeneratorBbackfromthegetAndSet()methodandremovestheScoreLabelfromthe
listenerstogeneratorB.Ifthesecondthreadexecutesfirst,thevariablesareslightlydifferent,buttheoutcomeisalwaysthesame:whicheverobjectis
assignedtothegeneratorinstancevariableistheone(andonlyone)objectthattheScoreLabelobjectislisteningto.
Thereisonesideeffectherethataffectsanothermethod.Sincethelistenerisremovedfromtheolddatasourceaftertheexchange,andthelistenerisaddedto
thenewdatasourcebeforetheexchange,itisnowpossibletoreceiveacharactereventthatisneitherfromthecurrentgeneratorortypistsource.The
newCharacter()methodpreviouslycheckedtoseewhetherthesourceisthegeneratorsource,andifnot,assumesitisthetypistsource.Thisisnolonger
valid.ThenewCharacter()methodnowneedstoconfirmthesourceofthecharacterbeforeprocessingititmustalsoignorecharactersfromspurious
listeners.
RETRYING OPERATIONS
ThenewCharacter()methodcontainsthemostextensivechangesinthisexample.Aswementioned,thefirstchangeistoseparateeventsbasedonthe
differentcharactersources.Thismethodcannolongerassumethatthesourceisthetypistifthesourceisnotthegenerator:itmustalsothrowawayanyevent
thatisfromneitheroftheattachedsources.
Thehandlingofthegeneratoreventhasonlyminorchanges.First,thegetAndSet()methodisusedtoexchangethecharacterwiththenewvalue
atomically.Second,theusercan'tbepenalizeduntilaftertheexchange.Thisisbecausethereisnowaytobesurewhatthepreviouscharacterwasuntilafter
theexchangeofthegetAndSet()methodcompletes.Furthermore,thescoremustalsobedecrementedatomicallysinceitcouldbechangedsimultaneously
bymultiplearrivingevents.Updatestothecharacterandscorearenothandledatomically:araceconditionstillexists.However,onceagainitisnota
problem.Weneedtoupdatethescoretocreditorpenalizetheusercorrectly.Itisnotaproblemiftheuserseesaveryshortdelaybeforethescoreisupdated.
Thehandlingofthetypisteventismorecomplicated.Weneedtochecktoseeifthecharacteristypedcorrectly.Ifitisn't,theuserispenalized.Thisis
accomplishedbydecrementingthescoreatomically.Ifthecharacteristypedcorrectly,theusercan'tbegivencreditimmediately.Instead,thechar2type
variablehastobeupdatedfirst.Thescoreisupdatedonlyifchar2typehasbeenupdatedcorrectly.Iftheupdateoperationfails,itmeansthatanotherevent
hasbeenprocessed(inanotherthread)whilewewereprocessingthiseventandthattheotheroperationwassuccessful.
Whatdoesitmeanthattheotherthreadwassuccessfulinprocessinganotherevent?Itmeansthatwemuststartoureventprocessingoverfromthe
beginning.Wemadecertainassumptionsaswewentalong:assumptionsthatthevalueofvariableswewereusingwouldn'tchangeandthatwhenourcode
wascompleted,allthevariableswehadsettohaveaparticularvaluewouldindeedhavethatvalue.Becauseoftheconflictwiththeotherthread,those
assumptionsareviolated.Byretryingtheeventprocessingfromthebeginning,it'sasifweneverraninthefirstplace.
That'swhythissectionofcodeiswrappedinanendlessloop:theprogramdoesnotleavetheloopuntiltheeventisprocessedsuccessfully.Obviously,there
isaraceconditionbetweenmultipleeventstheloopensuresthatnoneoftheeventsaremissedorprocessedmorethanonce.Aslongasweprocessallvalid
eventsexactlyonce,theorderinwhichtheeventsareprocesseddoesn'tmatter:afterprocessingeachevent,thedataisleftinaconsistentstate.Notethateven
whenweusesynchronization,thesamesituationapplies:multipleeventsarenotprocessedinaspecificordertheyareprocessedintheorderthatthelocks
aregranted.
Thepurposeofatomicvariablesistoavoidsynchronizationforthesakeofperformance.However,howcanatomicvariablesbefasterifwehavetoplacethe
codeinanendlessloop?Theanswer,ofcourse,isthattechnicallyitisnotanendlessloop.Extraiterationsoftheloopoccuronlyiftheatomicoperationfails,
whichinturnisduetoaconflictwithanotherthread.Forthelooptobetrulyendless,wewouldneedanendlessnumberofconflicts.Thatwouldalsobea
problemifweusedsynchronization:anendlessnumberofthreadsaccessingthelockwouldalsopreventtheprogramfromoperatingcorrectly.Ontheother
hand,asdiscussedinChapter14,thedifferenceinperformancebetweenatomicclassesandsynchronizationisoftennotthatlargetobeginwith.
Aswecantellfromthisexample,it'snecessarytobalancetheusageofsynchronizationandatomicvariables.Whenweusesynchronization,threadsare
blockedfromrunninguntiltheyacquirealock.Thisallowsthecodetoexecuteatomicallysinceotherthreadsarebarredfromrunningthatcode.Whenwe
useatomicvariables,threadsareallowedtoexecutethesamecodeinparallel.Thepurposeofatomicvariablesisnottoremoveraceconditionsthatarenot
threadsafetheirpurposeistomakethecodethreadsafesothattheraceconditiondoesnothavetobeprevented.
importjava.awt.*;
importjavax.swing.*;
importjava.util.concurrent.*;
importjava.util.concurrent.atomic.*;
importjavathreads.examples.ch05.*;
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
privateAtomicBooleandone=newAtomicBoolean(true);
privateAtomicIntegercurX=newAtomicInteger(0);
privateAtomicIntegertempChar=newAtomicInteger(0);
privateThreadtimer=null;
publicAnimatedCharacterDisplayCanvas(){
startAnimationThread();
}
publicAnimatedCharacterDisplayCanvas(CharacterSourcecs){
super(cs);
startAnimationThread();
}
privatevoidstartAnimationThread(){
if(timer==null){
timer=newThread(this);
timer.start();
}
}
publicvoidnewCharacter(CharacterEventce){
curX.set(0);
tempChar.set(ce.character);
repaint();
}
protectedvoidpaintComponent(Graphicsgc){
char[]localTmpChar=newchar[1];
localTmpChar[0]=(char)tempChar.get();
intlocalCurX=curX.get();
Dimensiond=getSize();
intcharWidth=fm.charWidth(localTmpChar[0]);
gc.clearRect(0,0,d.width,d.height);
if(localTmpChar[0]==0)
return;
gc.drawChars(localTmpChar,0,1,
localCurX,fontHeight);
curX.getAndIncrement();
}
publicvoidrun(){
while(true){
try{
Thread.sleep(100);
if(!done.get()){
repaint();
}
}catch(InterruptedExceptionie){
return;
}
}
}
publicvoidsetDone(booleanb){
done.set(b);
}
}
Aswithourpreviousexample,usingatomicvariablesisnotsimplyamatterofreplacingthevariablesprotectedbysynchronizationwithatomicvariables:the
algorithmalsoneedstobeadjustedinafashionthatallowsanyraceconditionstobethreadsafe.Inouranimationcomponent,thisisespeciallytrueforthe
codethatcreatestheanimationthread.OurpreviousexamplescreatedthisthreadwhenthesetDone()methodwascalled.Wecouldhaveleftthecodein
thatmethodandusedanatomicreferencevariabletostorethethreadobjectonlythethreadthatsuccessfullystoredtheatomicreferencewouldactuallycall
thestartmethodofthenewthread.However,it'smucheasiertoimplementthisfunctionalitybycreatingandstartingthethreadinaprivatemethodthatis
calledonlybytheconstructoroftheobject(sincetheconstructorcanneverbecalledbymultiplethreads).
ThenewCharacter()methodisonlypartiallyatomic.Theindividualvariableoperations,assignmentsofcurXandtempChar,areatomicsincetheyare
usingatomicvariables.However,bothassignmentstogetherarenotatomic.Thisisnotaproblemifanotherthreadsimultaneouslycallsthe
newCharacter()methodbothmethodcallssetthecurXvariabletozero,andthecharactervariableisassignedtothecharacterrequestedbythesecond
threadtoexecutethemethod.ThereisalsoaraceconditionbetweenthismethodandthepaintComponent()method,butitisprobablynoteven
noticeable.TheraceconditionhereresultsinaspuriousincrementbythepaintComponent()method.Thismeansthatthenewcharacterisdrawnstarting
withthesecondanimationframethefirstanimationframeisskippedaneffectthatisunlikelytobenoticedbytheuser.
ThepaintComponent()methodisalsonotcompletelyatomic,butaswiththenewCharacter()method,allitsraceconditionsareacceptable.Itisnot
possibleforthepaintComponent()methodtohaveaconflictwithitself,asthepaintComponent()methodiscalledonlybythewindowingsystem
andonlythenfromasinglethread.So,thereisnoreasontoprotectthevariablesthatareusedonlybythepaintComponent()method.The
paintComponent()methodloadsintotemporaryvariablesdatathatithasincommonwiththenewCharacter()method.Ifthosevariableshappento
changeduringthepaintComponent()methodcall,itisnotaproblemsinceanotherrepaint()requestwillalsobesentbythenewCharacter()
method.Theresultagainisjustaspuriousanimationframe.
Therun()methodissimilartoourpreviousversionsinthatitcallstherepaint()methodevery100millisecondswhilethedoneflagisfalse.However,
ifthedoneflagissettotrue,thethreadstillwakesupevery100milliseconds.Thismeansthattheprogramdoesa"nothing"taskevery100milliseconds.
Thisthreadalwaysexecutesevery100millisecondswhentheanimationisrunningitnowstillexecuteswhenthegameisstopped.Ontheotherhand,
resumingtheanimationisnolongerinstantaneous:theusercouldwaitasmuchas100millisecondstoseearestartoftheanimation.Thiscouldbesolvedby
callingtherepaint()methodfromthesetDone()method,butthatisnotnecessaryforthisexample.Thedelaybetweentheframesoftheanimationis
100milliseconds.Ifa100milliseconddelaytostarttheanimationisnoticeable,the100milliseconddelaybetweentheframeswillbejustasnoticeable.
TheimplementationofthesetDone()methodisnowmuchsimpler.Itnolongerneedstocreatetheanimationthreadsincethatisnowdoneduring
constructionofthecomponent.Anditnolongerneedstoinformtheanimationthreadthatthedoneflaghaschanged.
Themajorbenefitofthisimplementationisthatthereisnolongeranysynchronizationinthiscomponent.Thereisaslightthreadingoverheadwhenthegame
isnotrunning,butitisstilllessthanwhenthegameisrunning.Otherprogramsmayhaveadifferentprofile.Aswementioned,developersdonotjustfacea
choiceofusingsynchronizationtechniquesoratomicvariablestheymuststrikeabalancebetweenthetwo.Inordertounderstandthebalance,itisbeneficial
tousebothtechniquesformanycases.
DATA EXCHANGE
Dataexchangeistheabilitytosetavalueatomicallywhileobtainingthepreviousvalue.ThisisaccomplishedwiththegetAndSet()method.Usingthis
methodguaranteesthatonlyasinglethreadobtainsandusesavalue.
Whatifthedataexchangeismorecomplex?Whatifthevaluetobesetisdependentonthepreviousvalue?Thisishandledbyplacingtheget()andthe
compareAndSet()methodsinaloop.Theget()methodisusedtogetthepreviousvalue,whichisusedtocalculatethenewvalue.Thevariableissetto
thenewvalueusingthecompareAndSet()methodwhichsetsthenewvalueonlyifthevalueofthevariablehasnotchanged.Ifthe
compareAndSet()methodfails,theentireoperationcanberetriedbecausethecurrentthreadhasnotchangedanydatauptothetimeofthefailure.
Althoughtheget()methodcall,thecalculationofthenewvalue,andtheexchangeofdatamaynotbeindividuallyatomic,thesequenceisconsidered
atomiciftheexchangeissuccessfulsinceitcansucceedonlyifnootherthreadhaschangedthevalue.
Whatifthecomparisonismorecomplex?Whatifthecomparisonisdependentonthepreviousorexternalvalues?Thiscasecanbehandledasbeforeby
placingtheget()andthecompareAndSet()methodsinaloop.Theget()methodisusedtogetthepreviousvalue,whichcanbeusedeitherfor
comparisonorjusttoallowanatomicexchange.Thecomplexcomparisonisusedtoseeiftheoperationshouldproceed.ThecompareAndSet()method
isthenusedtosetthevalueifthecurrentvaluehasnotchanged.Thewholeoperationisretriediftheoperationfails.Asbefore,thewholeoperationis
consideredatomicbecausethedataischangedatomicallyandchangedonlyifitmatchesthevalueatthestartoftheoperation.
packagejavathreads.examples.ch05;
importjava.lang.*;
importjava.util.concurrent.atomic.*;
publicclassAtomicDoubleextendsNumber{
privateAtomicReference<Double>value;
publicAtomicDouble(){
this(0.0);
}
publicAtomicDouble(doubleinitVal){
value=newAtomicReference<Double>(newDouble(initVal));
}
publicdoubleget(){
returnvalue.get().doubleValue();
}
publicvoidset(doublenewVal){
value.set(newDouble(newVal));
}
publicbooleancompareAndSet(doubleexpect,doubleupdate){
DoubleorigVal,newVal;
newVal=newDouble(update);
while(true){
origVal=value.get();
if(Double.compare(origVal.doubleValue(),expect)==0){
if(value.compareAndSet(origVal,newVal))
returntrue;
}else{
returnfalse;
}
}
}
publicbooleanweakCompareAndSet(doubleexpect,doubleupdate){
returncompareAndSet(expect,update);
}
publicdoublegetAndSet(doublesetVal){
DoubleorigVal,newVal;
newVal=newDouble(setVal);
while(true){
origVal=value.get();
if(value.compareAndSet(origVal,newVal))
returnorigVal.doubleValue();
}
}
publicdoublegetAndAdd(doubledelta){
DoubleorigVal,newVal;
while(true){
origVal=value.get();
newVal=newDouble(origVal.doubleValue()+delta);
if(value.compareAndSet(origVal,newVal))
returnorigVal.doubleValue();
}
}
publicdoubleaddAndGet(doubledelta){
DoubleorigVal,newVal;
while(true){
origVal=value.get();
newVal=newDouble(origVal.doubleValue()+delta);
if(value.compareAndSet(origVal,newVal))
returnnewVal.doubleValue();
}
}
publicdoublegetAndIncrement(){
returngetAndAdd((double)1.0);
}
publicdoublegetAndDecrement(){
returngetAndAdd((double)1.0);
}
publicdoubleincrementAndGet(){
returnaddAndGet((double)1.0);
}
publicdoubledecrementAndGet(){
returnaddAndGet((double)1.0);
}
publicdoublegetAndMultiply(doublemultiple){
DoubleorigVal,newVal;
while(true){
origVal=value.get();
newVal=newDouble(origVal.doubleValue()*multiple);
if(value.compareAndSet(origVal,newVal))
returnorigVal.doubleValue();
}
}
publicdoublemultiplyAndGet(doublemultiple){
DoubleorigVal,newVal;
while(true){
origVal=value.get();
newVal=newDouble(origVal.doubleValue()*multiple);
if(value.compareAndSet(origVal,newVal))
returnnewVal.doubleValue();
}
}
}
InournewAtomicDoubleclass,weuseanatomicreferenceobjecttoencapsulateadoublefloatingpointvalue.SincetheDoubleclassalready
encapsulatesadoublevalue,thereisnoneedtocreateanewclasstheDoubleclassisusedtoholdthedoublevalue.
Theget()methodnowhastousetwomethodcallstogetthedoublevalueitmustnowgettheDoubleobject,whichinturnisusedtogetthedouble
floatingpointvalue.GettingtheDoubleobjecttypeisobviouslyatomicbecauseweareusinganatomicreferenceobjecttoholdtheobject.However,the
overalltechniqueworksbecausethedataisreadonly:itcan'tbechanged.Ifthedatawerenotreadonly,retrievalofthedatawouldnotbeatomic,andthetwo
methodswhenusedtogetherwouldalsonotbeconsideredatomic.
Theset()methodisusedtochangethevalue.Sincetheencapsulatedvalueisreadonly,wemustcreateanewDoubleobjectinsteadofchangingthe
previousvalue.Asfortheatomicreferenceitself,itisatomicbecauseweareusinganatomicreferenceobjecttochangethevalueofthereference.
ThecompareAndSet()methodisimplementedusingthecomplexcompareandsettechniquealreadymentioned.ThegetAndSet()methodis
implementedusingthecomplexdataexchangetechniquealreadymentioned.Andasforalltheothermethodsthemethodsthatadd,multiply,etc.they
too,areimplementedusingthecomplexdataexchangetechnique.Wedon'texplicitlyshowanexampleinthischapterforthisclass,butwe'lluseitinChapter
15.Fornow,thisclassisagreatframeworkforimplementingatomicsupportfornewandcomplexdatatypes.
packagejavathreads.examples.ch05.example3;
importjava.util.concurrent.atomic.*;
publicclassAtomicScoreAndCharacter{
publicclassScoreAndCharacter{
privateintscore,char2type;
publicScoreAndCharacter(intscore,intchar2type){
this.score=score;
this.char2type=char2type;
}
publicintgetScore(){
returnscore;
}
publicintgetCharacter(){
returnchar2type;
}
}
privateAtomicReference<ScoreAndCharacter>value;
publicAtomicScoreAndCharacter(){
this(0,1);
}
publicAtomicScoreAndCharacter(intinitScore,intinitChar){
value=newAtomicReference<ScoreAndCharacter>
(newScoreAndCharacter(initScore,initChar));
}
publicintgetScore(){
returnvalue.get().getScore();
}
publicintgetCharacter(){
returnvalue.get().getCharacter();
}
publicvoidset(intnewScore,intnewChar){
value.set(newScoreAndCharacter(newScore,newChar));
}
publicvoidsetScore(intnewScore){
ScoreAndCharacterorigVal,newVal;
while(true){
origVal=value.get();
newVal=newScoreAndCharacter
(newScore,origVal.getCharacter());
if(value.compareAndSet(origVal,newVal))break;
}
}
publicvoidsetCharacter(intnewCharacter){
ScoreAndCharacterorigVal,newVal;
while(true){
origVal=value.get();
newVal=newScoreAndCharacter
(origVal.getScore(),newCharacter);
if(value.compareAndSet(origVal,newVal))break;
}
}
publicvoidsetCharacterUpdateScore(intnewCharacter){
ScoreAndCharacterorigVal,newVal;
intscore;
while(true){
origVal=value.get();
score=origVal.getScore();
score=(origVal.getCharacter()==1)?score:score1;
newVal=newScoreAndCharacter(score,newCharacter);
if(value.compareAndSet(origVal,newVal))break;
}
}
publicbooleanprocessCharacter(inttypedChar){
ScoreAndCharacterorigVal,newVal;
intorigScore,origCharacter;
booleanretValue;
while(true){
origVal=value.get();
origScore=origVal.getScore();
origCharacter=origVal.getCharacter();
if(typedChar==origCharacter){
origCharacter=1;
origScore++;
retValue=true;
}else{
origScore;
retValue=false;
}
newVal=newScoreAndCharacter(origScore,origCharacter);
if(value.compareAndSet(origVal,newVal))break;
}
returnretValue;
}
}
AsinourAtomicDoubleclass,thegetScore()andgetCharacter()methodsworkbecausetheencapsulatedvaluesaretreatedasreadonly.The
set()methodhastocreateanewobjecttoencapsulatethenewvaluestobestored.
ThesetScore()andsetCharacter()methodsareimplementedusingtheadvancedataexchangetechnique.Thisisbecausetheimplementationis
technicallyexchangingdata,notjustsettingthedata.Eventhoughwearechangingonlyonepartoftheencapsulateddata,westillhavetoreadthedatathatis
notsupposedtochange(inordertomakesurethat,infact,ithasn't).Andsincewehavetochangethewholesetofdataatomicallyguaranteeingthatthedata
thatisn'tsupposedtochangedidnotchangewehavetoimplementthecodeasadataexchange.
ThesetCharacterUpdateScore()andprocessCharacter()methodsimplementthecoreofthescoringsystem.Thefirstmethodsetsthenew
charactertobetypedwhilepenalizingtheuserifthepreviouscharacterhasnotbeentypedcorrectly.Thesecondmethodcomparesthetypedcharacterwith
thecurrentgeneratedcharacter.Iftheymatch,thecharacterissettoanoncharactervalue,andthescoreisincremented.Iftheydonotmatch,thescoreis
simplydecremented.Interestingly,ascomplexasthesetwomethodsare,theyarestillatomic,becauseallcalculationsaredonewithtemporaryvariablesand
allofthevaluesareatomicallychangedusingadataexchange.
Performingbulkdatamodification,aswellasusinganadvancedatomicdatatype,mayusealargenumberofobjects.Anewobjectneedstobecreatedfor
everytransaction,regardlessofhowmanyvariablesneedtobemodified.Anewobjectalsoneedstobecreatedforeachatomiccompareandsetoperation
thatfailsandhastoberetried.Onceagain,usingatomicvariableshastobebalancedwithusingsynchronization.Isthecreationofallthetemporaryobjects
acceptable?Isthistechniquebetterthansynchronization?Oristhereacompromise?Theanswerdependsonyourparticularprogram.
Asthesetechniquesdemonstrate,usingatomicvariablesissometimescomplex.Thecomplexityoccurswhenyouusemultipleatomicvariables,multiple
operationsonasingleatomicvariable,orbothtechniqueswithinasectionofcodethatmustbeatomic.Inmanycases,atomicvariablesaresimpletouse
becauseyoujustwanttousethemforasingleoperation,suchasupdatingascore.
Inmanycases,usingthiskindofminimalsynchronizationisnotagoodidea.Itcangetverycomplex,makingitdifficultforthecodetobemaintainedor
transferredbetweendevelopers.Withahighvolumeofmethodcallswheresynchronizationcanbeaproblem,thebenefittominimalsynchronizationisstill
debatable.Forthosereadersthatfindaclassorsubsystemwheretheybelievesynchronizationiscausingaproblem,itmaybeagoodideatorevisitthistopic
ifjusttogetabettercomfortlevelinusingminimalsynchronization.
publicclassThreadLocal<T>{
protectedTinitialValue();
publicTget();
publicvoidset(Tvalue);
publicvoidremove();
}
Intypicalusage,yousubclasstheThreadLocalclassandoverridetheinitialValue()methodtoreturnthevaluethatshouldbereturnedthefirsttime
athreadaccessesthevariable.ThesubclassrarelyneedstooverridetheothermethodsoftheThreadLocalclassinstead,thosemethodsareusedasa
getter/setterpatternforthethreadspecificvalue.
Onecasewhereyoumightuseathreadlocalvariabletoavoidsynchronizationisinathreadspecificcache.Considerthefollowingclass:
packagejavathreads.examples.ch05.example4;
importjava.util.*;
publicabstractclassCalculator{
privatestaticThreadLocal<HashMap>results=newThreadLocal<HashMap>(){
protectedHashMapinitialValue(){
returnnewHashMap();
}
};
publicObjectcalculate(Objectparam){
HashMaphm=results.get();
Objecto=hm.get(param);
if(o!=null)
returno;
o=doLocalCalculate(param);
hm.put(param,o);
returno;
}
protectedabstractObjectdoLocalCalculate(Objectparam);
}
Threadlocalobjectsaredeclaredstaticsothattheobjectitself(thatis,theresultsvariableinthisexample)issharedamongallthreads.Whentheget()
methodofthethreadlocalvariableiscalled,theinternalmechanismofthethreadlocalclassreturnsthespecificobjectassignedtothespecificthread.The
initialvalueofthatobjectisreturnedfromtheinitialValue()methodoftheclassextendingThreadLocalwhenyoucreateathreadlocalvariable,you
areresponsibleforimplementingthatmethodtoreturntheappropriate(threadspecific)object.
Whenthecalculate()methodinourexampleiscalled,thethreadlocalhashmapisconsultedtoseeifthevaluehaspreviouslybeencalculated.Ifso,that
valueisreturnedotherwise,thecalculationisperformedandthenewvaluestoredinthehashmap.Sinceaccesstothemapisfromonlyasinglethread,we're
abletouseaHashMapobjectratherthanaHashtableobject(orotherwisesynchronizingthehashmap).
Thisapproachisworthwhileonlyifthecalculationisveryexpensivesinceobtainingthehashmapitselfrequiressynchronizingonallthethreads.Ifthe
referencereturnedfromthethreadlocalget()methodisheldalongtime,itmaybeworthexploringthistypeofdesignsinceotherwisethatreferencewould
needtobesynchronizedforalongtime.Otherwise,you'rejusttradingonesynchronizationcallforanother.Andingeneral,theperformanceofthe
ThreadLocalclasshasbeenfairlydismal,thoughthissituationimprovedinJDK1.4andevenmoreinJ2SE5.0.
Anothercasewherethistechniqueisusefulisdealingwiththreadunsafeclasses.Ifeachthreadinstantiatesthenecessaryobjectinathreadlocalvariable,it
hasitsowncopythatitcansafelyaccess.
Thisclassallowsachildthreadtoinheritthevalueofthethreadlocalvariablefromitsparentthatis,whentheget()methodofthethreadlocalvariableis
calledbythechildthread,itreturnsthesamevalueaswhenthatmethodiscalledbytheparentthread.
Ifyoulike,youcanusethechildValue()methodtofurtheraugmentthisbehavior.Whenthechildthreadcallstheget()methodofthethreadlocal
variable,theget()methodlooksupthevalueassociatedwiththeparentthread.ItthenpassesthatvaluetothechildValue()methodandreturnsthat
result.Bydefault,thechildValue()methodsimplyreturnsitsargument,sonotransformationoccurs.
Summary
Inthischapter,we'veexaminedsomeadvancedtechniquesforsynchronization.We'velearnedabouttheJavamemorymodelandwhyitinhibitssome
synchronizationtechniquesfromworkingasexpected.Thishasledtoabetterunderstandingofvolatilevariablesaswellasanunderstandingofwhyit'shard
tochangethesynchronizationrulesimposedbyJava.
We'vealsoexaminedtheatomicpackagethatcomeswithJ2SE5.0.Thisisonewayinwhichsynchronizationcanbeavoided,butitcomeswithaprice:the
natureoftheclassesintheatomicpackageissuchthatalgorithmsthatusethemoftenhavetochange(particularlywhenmultipleatomicvariablesareusedat
once).Creatingamethodthatloopsuntilthedesiredoutcomeisachievedisacommonwaytoimplementatomicvariables.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
SwingTypeTesterusingatomicScoreLabel
javathreads.examples.ch05.example1.SwingTypeTester
ch5ex1
SwingTypeTesterusingatomicanimationcanvas
javathreads.examples.ch05.example2.SwingTypeTester
ch5ex2
SwingTypeTesterusingatomicscoreandcharacterclass
javathreads.examples.ch05.example3.SwingTypeTester
ch5ex3
Calculationtestusingthreadlocalvariables
javathreads.examples.ch05.example4.CalculatorTest
ch5ex4
Thecalculatortestrequiresacommandlineargumentthatsetsthenumberofthreadsthatrunsimultaneously.IntheAntscript,itisdefinedbythisproperty:
<propertyname="CalcThreadCount"value="10"/>
[ 1 ]
Thevirtualmachinecanuseregistersforvolatilevariablesaslongasitobeysthesemanticswe'veoutlined.It'stheprinciplethatmust
beobeyed,nottheactualimplementation.
[ 2 ]
TheScoreLabelclassalsomarksourfirstexampleusingtheJ2SE5.0genericsfeature.You'llbegintoseeparameterizedcodein
anglebracketsinthisclass<CharacterSource>isagenericreference.Formoredetails,seeJava1.5Tiger:ADeveloper'sNotebookby
DavidFlanaganandBrettMcLaughlin(O'Reilly).
Synchronization Terms
Programmerswithabackgroundinaparticularthreadingsystemgenerallytendtousetermsspecifictothatsystemtorefertosomeoftheconceptswe
discussinthischapter,andprogrammerswithoutabackgroundincertainthreadingsystemsmaynotnecessarilyunderstandthetermsweuse.Sohere'sa
comparisonofparticulartermsyoumaybefamiliarwithandhowtheyrelatetothetermsinthischapter:
Barrier
Abarrierisarendezvouspointformultiplethreads:allthreadsmustarriveatthebarrierbeforeanyofthemarepermittedtoproceedpastthebarrier.
J2SE5.0suppliesabarrierclass,andabarrierclassforpreviousversionsofJavacanbefoundintheAppendixA.
Conditionvariable
Aconditionvariableisnotactuallyalockitisavariableassociatedwithalock.Conditionvariablesareoftenusedinthecontextofdatasynchronization.
ConditionvariablesgenerallyhaveanAPIthatachievesthesamefunctionalityasJava'swaitandnotifymechanisminthatmechanism,thecondition
variableisactuallytheobjectlockitisprotecting.J2SE5.0alsosuppliesexplicitconditionvariables,andaconditionvariableimplementationforprevious
versionsofJavacanbefoundintheAppendixA.BothkindsofconditionvariablesarediscussedinChapter4.
Criticalsection
Acriticalsectionisasynchronizedmethodorblock.Criticalsectionsdonotnestlikesynchronizedmethodsorblocks.
Eventvariable
Eventvariableisanothertermforaconditionvariable.
Lock
Thistermreferstotheaccessgrantedtoaparticularthreadthathasenteredasynchronizedmethodorblock.Wesaythatathreadthathasenteredsucha
methodorblockhasacquiredthelock.AswediscussedinChapter3,alockisassociatedwitheitheraparticularinstanceofanobjectoraparticularclass.
Monitor
Agenericsynchronizationtermusedinconsistentlybetweenthreadingsystems.Insomesystems,amonitorissimplyalockinothers,amonitoris
similartothewaitandnotifymechanism.
Mutex
Anothertermforalock.Mutexesdonotnestlikesynchronizationmethodsorblocksandgenerallycanbeusedacrossprocessesattheoperatingsystem
level.
Reader/writerlocks
Alockthatcanbeacquiredbymultiplethreadssimultaneouslyaslongasthethreadsagreetoonlyreadfromtheshareddataorthatcanbeacquiredbya
singlethreadthatwantstowritetotheshareddata.J2SE5.0suppliesareaderwriterlockclass,andasimilarclassforpreviousversionsofJavacanbe
foundintheAppendixA.
Semaphores
Semaphoresareusedinconsistentlyincomputersystems.ManydevelopersusesemaphorestolockobjectsinthesamewayJavalocksareusedthis
usagemakesthemequivalenttomutexes.Amoresophisticateduseofsemaphoresistotakeadvantageofacounterassociatedwiththemtonest
acquisitionstothecriticalsectionsofcodeJavalocksareexactlyequivalenttosemaphoresinthisusage.Semaphoresarealsousedtogainaccessto
resourcesotherthancode.SemaphoreclassesthatimplementmostofthesefeaturesareavailableinJ2SE5.0.
Semaphore
InJava,asemaphoreisbasicallyalockwithanattachedcounter.ItissimilartotheLockinterfaceasitcanalsobeusedtopreventaccessifthelockis
grantedthedifferenceisthecounter.Inthoseterms,asemaphorewithacounterofoneisthesamethingasalock(exceptthatthesemaphorewouldnotnest,
whereasthelockdependingonitsimplementationmight).
TheSemaphoreclasskeepstracksofthenumberofpermitsitcanissue.Itallowsmultiplethreadstograboneormorepermitstheactualusageofthe
permitsisuptothedeveloper.Therefore,asemaphorecanbeusedtorepresentthenumberoflocksthatcanbegranted.Itcouldalsobeusedtothrottlethe
numberofthreadsworkinginparallel,duetoresourcelimitationssuchasnetworkconnectionsordiskspace.
Let'stakealookattheSemaphoreinterface:
publicclassSemaphore{
publicSemaphore(longpermits);
publicSemaphore(longpermits,booleanfair);
publicvoidacquire()throwsInterruptedException;
publicvoidacquireUninterruptibly();
publicvoidacquire(longpermits)throwsInterruptedException;
publicvoidacquireUninterruptibly(longpermits);
publicbooleantryAcquire();
publicbooleantryAcquire(longtimeout,TimeUnitunit);
publicbooleantryAcquire(longpermits);
publicbooleantryAcquire(longpermits,
longtimeout,TimeUnitunit);
publicvoidrelease(longpermits);
publicvoidrelease();
publiclongavailablePermits();
}
TheSemaphoreinterfaceisverysimilartotheLockinterface.Theacquire()andrelease()methodsaresimilartothelock()andunlock()
methodsoftheLockinterfacetheyareusedtograbandreleasepermits,respectively.ThetryAcquire()methodsaresimilartothetryLock()
methodsinthattheyallowthedevelopertotrytograbthelockorpermits.Thesemethodsalsoallowthedevelopertospecifythetimetowaitifthepermits
arenotimmediatelyavailableandthenumberofpermitstoacquireorrelease(thedefaultnumberofpermitsisone).
Semaphoreshaveafewdifferencesfromlocks.First,theconstructorrequiresthespecificationofthenumberofpermitstobegranted.Therearealso
methodsthatreturnthenumberoftotalandfreepermits.ThisclassimplementsonlyagrantandreleasealgorithmunliketheLockinterface,noattached
conditionvariablesareavailablewithsemaphores.Thereisnoconceptofnestingmultipleacquisitionsbythesamethreadacquiremultiplepermitsfromthe
semaphore.
Ifasemaphoreisconstructedwithitsfairflagsettotrue,thesemaphoretriestoallocatethepermitsintheorderthattherequestsaremadeascloseto
firstcomefirstserveaspossible.Thedownsidetothisoptionisspeed:ittakesmoretimeforthevirtualmachinetoordertheacquisitionofthepermitsthan
toallowanarbitrarythreadtoacquireapermit.
Barrier
Ofallthedifferenttypesofthreadsynchronizationtools,thebarrierisprobablytheeasiesttounderstandandtheleastused.Whenwethinkof
synchronization,ourfirstthoughtisofagroupofthreadsexecutingpartofanoveralltaskfollowedbyapointatwhichtheymustsynchronizetheirresults.
Thebarrierissimplyawaitingpointwhereallthethreadscansyncupeithertomergeresultsortosafelymoveontothenextpartofthetask.Thisis
generallyusedwhenanapplicationoperatesinphases.Forexample,manycompilersmakemultiplepassesbetweenloadingthesourceandgeneratingthe
executable,withmanyinterimfiles.Abarrier,whenusedinthisregard,canmakesurethatallofthethreadsareinthesamephase.
Givenitssimplicity,whyisthebarriernotmorecommonlyused?Thefunctionalityissimpleenoughthatitcanbeaccomplishedwiththelowleveltools
providedbyJava.Wecansolvethecoordinationproblemintwoways,withoutusingabarrier.First,wecansimplyhavethethreadswaitonacondition
variable.Thelastthreadreleasesthebarrierbynotifyingalloftheotherthreads.Asecondoptionistosimplyawaitterminationofthethreadsbyusingthe
join()method.Onceallthreadshavebeenjoined,wecanstartnewthreadsforthenextphaseoftheprogram.
However,insomecasesitispreferabletousebarriers.Whenusingthejoin()method,threadsareexitingandwe'restartingnewones.Therefore,the
threadsloseanystatethattheyhavestoredintheirpreviousthreadobjecttheyneedtostorethatstatepriortoterminating.Furthermore,ifwemustalways
createnewthreads,logicaloperationscannotbeplacedtogethersincenewthreadshavetobecreatedforeachsubtask,thecodeforeachsubtaskmustbe
placedinseparaterun()methods.Itmaybeeasiertocodeallofthelogicasonemethod,particularlyifthesubtasksareverysmall.
Let'sexaminetheinterfacetothebarrierclass:
publicclassCyclicBarrier{
publicCyclicBarrier(intparties);
publicCyclicBarrier(intparties,RunnablebarrierAction);
publicintawait()throwsInterruptedException,BrokenBarrierException;
publicintawait(longtimeout,TimeUnitunit)throwsInterruptedException,
BrokenBarrierException,TimeoutException;
publicvoidreset();
publicbooleanisBroken();
publicintgetParties();
publicintgetNumberWaiting();
}
Thecoreofthebarrieristheawait()method.Thismethodbasicallybehavesliketheconditionalvariable'sawait()method.Thereisanoptiontoeither
waituntilthebarrierreleasesthethreadorforatimeoutcondition.Thereisnoneedtohaveasignal()methodbecausenotificationisaccomplishedbythe
barrierwhenthecorrectnumberofpartiesarewaiting.
Whenthebarrierisconstructed,thedevelopermustspecifythenumberofparties(threads)usingthebarrier.Thisnumberisusedtotriggerthebarrier:the
threadsareallreleasedwhenthenumberofthreadswaitingonthebarrierisequaltothenumberofpartiesspecified.Thereisalsoanoptiontospecifyan
actionanobjectthatimplementstherun()method.Whenthetriggeroccurs,therun()methodonthebarrierActionobjectiscalledpriorto
releasingthethreads.Thisallowscodethatisnotthreadsafetoexecutegenerally,itcallsthecleanupcodeforthepreviousphaseand/orsetupcodeforthe
nextphase.Thelastthreadthatreachesthebarrierthetriggeringthreadisthethreadthatexecutestheaction.
Eachthreadthatcallstheawait()methodgetsbackauniquereturnvalue.Thisvalueisrelatedtothearrivalorderofthethreadatthebarrier.Thisvalueis
neededforcaseswhentheindividualthreadsneedtonegotiatehowtodivideupworkduringthenextphaseoftheprocess.Thefirstthreadtoarriveisone
lessthanthenumberofpartiesthelastthreadtoarrivewillhaveavalueofzero.
Innormalusage,thebarrierisverysimple.Allthethreadswaituntilthenumberofrequiredpartiesarrive.Uponarrivalofthelastthread,theactionis
executed,thethreadsarereleased,andthebarriercanbereused.However,exceptionconditionscanoccurandcausethebarriertofail.Whenthebarrierfails,
theCyclicBarrierclassbreaksthebarrierandreleasesallofthethreadswaitingontheawait()methodwithaBrokenBarrierException.The
barriercanbebrokenforanumberofreasons.Thewaitingthreadscanbeinterrupted,athreadmaybreakthroughthebarrierduetoatimeoutcondition,oran
exceptioncouldbethrownbythebarrieraction.
Ineveryexceptioncondition,thebarriersimplybreaks,thusrequiringthattheindividualthreadsresolvethematter.Furthermore,thebarriercannolongerbe
reuseduntilitisreinitialized.Thatis,partofthecomplex(andapplicationspecific)algorithmtoresolvethesituationincludestheneedtoreinitializethe
barrier.Toreinitializethebarrier,youusethereset()method.However,iftherearethreadsalreadywaitingonthebarrier,thebarrierwillnotinitializein
fact,itwillbreak.Reinitializationofthebarrieriscomplexenoughthatitmaybesafertocreateanewbarrier.
Finally,theCyclicBarrierclassprovidesafewoperationalsupportmethods.Thesemethodsprovideinformationaldataonthenumberofthreadsalready
waitingonthebarrier,orwhetherthebarrierisalreadybroken.
Countdown Latch
Thecountdownlatchimplementsasynchronizationtoolthatisverysimilartoabarrier.Infact,itcanbeusedinsteadofabarrier.Italsocanbeusedto
implementafunctionalitythatsomethreadingsystems(butnotJava)supportwithsemaphores.Likethebarrierclass,methodsareprovidedthatallow
threadstowaitforacondition.Thedifferenceisthatthereleaseconditionisnotthenumberofthreadsthatarewaiting.Instead,thethreadsarereleasedwhen
thespecifiedcountreacheszero.
TheCountDownLatchclassprovidesamethodtodecrementthecount.Itcanbecalledmanytimesbythesamethread.Itcanalsobecalledbyathreadthat
isnotwaiting.Whenthecountreacheszero,allwaitingthreadsarereleased.Itmaybethatnothreadsarewaiting.Itmaybethatmorethreadsthanthe
specifiedcountarewaiting.Andanythreadthatattemptstowaitafterthelatchhastriggeredisimmediatelyreleased.Thelatchdoesnotreset.Furthermore,
laterattemptstolowerthecountwillnotwork.
Here'stheinterfaceofthecountdownlatch:
publicclassCountDownLatch{
publicCountDownLatch(intcount);
publicvoidawait()throwsInterruptedException;
publicbooleanawait(longtimeout,TimeUnitunit)
throwsInterruptedException;
publicvoidcountDown();
publiclonggetCount();
}
Thisinterfaceisprettysimple.Theinitialcountisspecifiedintheconstructor.Acoupleofoverloadedmethodsareprovidedforthreadstowaitforthecount
toreachzero.Andacoupleofmethodsareprovidedtocontrolthecountonetodecrementandonetoretrievethecount.Thebooleanreturnvalueforthe
timeoutvariantoftheawait()methodindicateswhetherthelatchwastriggereditreturnstrueifitisreturningbecausethelatchwasreleased.
Exchanger
Theexchangerimplementsasynchronizationtoolthatdoesnotreallyhaveequivalentsinanyotherthreadingsystem.Theeasiestdescriptionofthistoolis
thatitisacombinationofabarrierwithdatapassing.Itisabarrierinthatitallowspairsofthreadstorendezvouswitheachotheruponmeetinginpairs,it
thenallowthepairstoexchangeonesetofdatawitheachotherbeforeseparating.
Thisclassisclosertoacollectionclassthanasynchronizationtoolitismainlyusedtopassdatabetweenthreads.Itisalsoveryspecificinthatthreadshave
tobepairedup,andaspecificdatatypemustbeexchanged.Butthisclassdoeshaveitsadvantages.Hereisitsinterface:
publicclassExchanger<V>{
publicExchanger();
publicVexchange(Vx)throwsInterruptedException;
publicVexchange(Vx,longtimeout,TimeUnitunit)
throwsInterruptedException,TimeoutException;
}
Theexchange()methodiscalledwiththedataobjecttobeexchangedwithanotherthread.Ifanotherthreadisalreadywaiting,theexchange()method
returnswiththeotherthread'sdata.Ifnootherthreadiswaiting,theexchange()methodwaitsforone.Atimeoutoptioncancontrolhowlongthecalling
threadwaits.
Unlikethebarrierclass,thisclassisverysafetouse:itwillnotbreak.Itdoesnotmatterhowmanypartiesareusingthisclasstheyarepairedupasthe
threadscomein.Timeoutsandinterruptsalsodonotbreaktheexchangerastheydointhebarrierclasstheysimplygenerateanexceptioncondition.The
exchangercontinuestopairthreadsaroundtheexceptioncondition.
Reader/Writer Locks
Sometimesyouneedtoreadinformationfromanobjectinanoperationthatmaytakeafairlylongtime.Youneedtolocktheobjectsothattheinformation
youreadisconsistent,butyoudon'tnecessarilyneedtopreventanotherthreadfromalsoreadingdatafromtheobjectatthesametime.Aslongasallthe
threadsareonlyreadingthedata,there'snoreasonwhytheyshouldn'treadthedatainparallelsincethisdoesn'taffectthedataeachthreadisreading.
Infact,theonlytimeweneeddatalockingiswhendataisbeingchanged,thatis,whenitisbeingwritten.Changingthedataintroducesthepossibilitythata
threadreadingthedataseesthedatainaninconsistentstate.Untilnow,we'vebeencontenttohavealockthatallowsonlyasinglethreadtoaccessthedata
whetherthethreadisreadingorwriting,basedonthetheorythatthelockisheldforashorttime.
Ifthelockneedstobeheldforalongtime,itmakessensetoconsiderallowingmultiplethreadstoreadthedatasimultaneouslysothatthesethreadsdon't
needtocompeteagainsteachothertoacquirethelock.Ofcourse,wemuststillallowonlyasinglethreadtowritethedata,andwemustmakesurethatnone
ofthethreadsthatwerereadingthedataarestillactivewhileoursinglewriterthreadischangingtheinternalstateofthedata.
HerearetheclassesandinterfacesinJ2SE5.0thatimplementthistypeoflocking:
publicinterfaceReadWriteLock{
LockreadLock();
LockwriteLock();
}
publicclassReentrantReadWriteLockimplementsReadWriteLock{
publicReentrantReadWriteLock();
publicReentrantReadWriteLock(booleanfair);
publicLockwriteLock();
publicLockreadLock();
}
YoucreateareaderwriterlockbyinstantiatinganobjectusingtheReentrantReadWriteLockclass.LiketheReentrantLockclass,anoptionallows
thelockstobedistributedinafairfashion.By"fair,"thisclassmeansthatthelockisgrantedonveryclosetoafirstcomefirstservebasis.Whenthelockis
released,thenextsetofreaders/writerisgrantedthelockbasedonarrivaltime.
Usageofthelockispredictable.Readersshouldobtainthereadlockwhilewritersshouldobtainthewritelock.BothoftheselocksareobjectsoftheLock
classtheirinterfaceisdiscussedinChapter3.Thereisonemajordifference,however:readerwriterlockshavedifferentsupportforconditionvariables.
YoucanobtainaconditionvariablerelatedtothewritelockbycallingthenewCondition()methodcallingthatmethodonareadlockgeneratesan
UnsupportedOperationException.
Theselocksalsonest,whichmeansthatownersofthelockcanrepeatedlyacquirethelocksasnecessary.Thisallowsforcallbacksorothercomplex
algorithmstoexecutesafely.Furthermore,threadsthatownthewritelockcanalsoacquirethereadlock.Thereverseisnottrue.Threadsthatowntheread
lockcannotacquirethewritelockupgradingthelockisnotallowed.However,downgradingthelockisallowed.Thisisaccomplishedbyacquiringtheread
lockbeforereleasingthewritelock.
Laterinthischapter,weexaminethetopicoflockstarvationindepth.Readerwriterlockshavespecialissuesinthisregard.
Inthissection,we'veexaminedhigherlevelsynchronizationtoolsprovidedbyJ2SE5.0.Thesetoolsallprovidefunctionalitythatinthepastcouldhavebeen
implementedbythebasetoolsprovidedbyJavaeitherthroughanimplementationbythedeveloperorbytheuseofthirdpartylibraries.Theseclassesdon't
providenewfunctionalitythatcouldn'tbeaccomplishedinthepastthesetoolsarewrittentotallyinJava.Inasense,theycanbeconsideredconvenience
classesthatis,theyaredesignedtomakedevelopmenteasierandtoallowapplicationdevelopmentatahigherlevel.
Thereisalsoalotofoverlapbetweentheseclasses.ASemaphorecanbeusedtopartiallysimulateaLocksimplybydeclaringasemaphorewithone
permit.Thewritelockofareaderwriterlockispracticallythesameasamutuallyexclusivelock.Asemaphorecanbeusedtosimulateareaderwriterlock,
withalimitedsetofreaders,simplybyhavingthereaderthreadacquireonepermitwhilethewriterthreadacquiresallthepermits.Acountdownlatchcanbe
usedasabarriersimplybyhavingeachthreaddecrementthecountpriortowaiting.
Themajoradvantageinusingtheseclassesisthattheyoffloadthreadinganddatasynchronizationissues.Developersshoulddesigntheirprogramsatashigh
alevelaspossibleandnothavetoworryaboutlowlevelthreadingissues.Thepossibilityofdeadlock,lockandCPUstarvation,andotherverycomplex
issuesismitigatedsomewhat.Usingtheselibraries,however,doesnotremovetheresponsibilityfortheseproblemsfromthedeveloper.
Preventing Deadlock
Deadlockbetweenthreadscompetingforthesamesetoflocksisthehardestproblemtosolveinanythreadedprogram.It'sahardenoughproblem,infact,
thatitcannotbesolvedinthegeneralcase.Instead,wetrytoofferagoodunderstandingofdeadlockandsomeguidelinesonhowtopreventit.Preventing
deadlockiscompletelytheresponsibilityofthedevelopertheJavavirtualmachinedoesnotdodeadlockpreventionordeadlockdetectiononyourbehalf.
Let'srevisitthesimpledeadlockexamplefromChapter3.
packagejavathreads.examples.ch03.example8;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
privateLockadminLock=newReentrantLock();
privateLockcharLock=newReentrantLock();
privateLockscoreLock=newReentrantLock();
...
publicvoidresetScore(){
try{
charLock.lock();
scoreLock.lock();
score=0;
char2type=1;
setScore();
}finally{
charLock.unlock();
scoreLock.unlock();
}
}
publicvoidnewCharacter(CharacterEventce){
try{
scoreLock.lock();
charLock.lock();
//Previouscharacternottypedcorrectly:1pointpenalty
if(ce.source==generator){
if(char2type!=1){
score;
setScore();
}
char2type=ce.character;
}
//Ifcharacterisextraneous:1pointpenalty
//Ifcharacterdoesnotmatch:1pointpenalty
else{
if(char2type!=ce.character){
score;
}else{
score++;
char2type=1;
}
setScore();
}
}finally{
scoreLock.unlock();
charLock.unlock();
}
}
}
Toreview,deadlockoccursiftwothreadsexecutethenewCharacter()andresetScore()methodsinafashionthateachcangrabonlyonelock.Ifthe
newCharacter()methodgrabsthescorelockwhiletheresetScore()methodgrabsthecharacterlock,theybotheventuallywaitforeachotherto
releasethelocks.Thelocks,ofcourse,arenotreleaseduntiltheycanfinishexecutionofthemethods.Andneitherthreadcancontinuebecauseeachiswaiting
fortheotherthread'slock.Thisdeadlockconditioncannotberesolvedautomatically.
Aswementionedatthetime,thisexampleissimple,butmorecomplicatedconditionsofdeadlockfollowthesameprinciplesoutlinedhere:they'reharderto
detect,butnothingmoreisinvolvedthantwoormorethreadsattemptingtoacquireeachother'slocks(or,morecorrectly,waitingforconflictingconditions).
Deadlockisdifficulttodetectbecauseitcaninvolvemanyclassesthatcalleachother'ssynchronizedsections(thatis,synchronizedmethodsorsynchronized
blocks)inanorderthatisn'tapparentlyobvious.Supposewehave26classes,AtoZ,andthatthesynchronizedmethodsofclassAcallthoseofclassB,
thoseofclassBcallthoseofclassC,andsoon,untilthoseofclassZcallthoseofclassA.Iftwothreadscallanyoftheseclasses,thiscouldleadusintothe
samesortofdeadlocksituationthatwehadbetweenthenewCharacter()andresetScore()methods,butit'sunlikelythataprogrammerexamining
thesourcecodewoulddetectthatdeadlock.
Nonetheless,acloseexaminationofthesourcecodeistheonlyoptionpresentlyavailabletodeterminewhetherdeadlockisapossibility.Javavirtualmachines
donotdetectdeadlockatruntime,andwhileitispossibletodeveloptoolsthatexaminesourcecodetodetectpotentialdeadlocksituations,nosuchtoolsexist
yetforJava.
Thesimplestwaytoavoiddeadlockistofollowthisrule.Whenalockisheld,nevercallanymethodsthatneedotherlocksi.e.,nevercallasynchronized
methodofanotherclassfromasynchronizedmethod.Thisisagoodrulethatisoftenadvocated,butit'snottheidealrulefortworeasons:
It'simpractical:manyusefulJavamethodsaresynchronized,andyou'llwanttocallthemfromyoursynchronizedmethod.Asanexample,manyofthe
collectionclassesdiscussedinChapter8havesynchronizedmethods.Toavoidtheusageofcollectionclassesfromsynchronizedmethodswouldprevent
datafrombeingmovedorresultsfrombeingsaved.
It'soverkill:ifthesynchronizedmethodyou'regoingtocalldoesnotinturncallanothersynchronizedmethod,there'snowaythatdeadlockcanoccur.
Furthermore,iftheclassorlibraryisaccessedonlythroughitsclassinterfacewithnocrosscallingplacingextrarestrictionsonusingthelibraryis
silly.
Nonetheless,ifyoucanmanagetoobeythisrule,therewillbenodeadlocksinyourprogram.
Anotherfrequentlyusedtechniquetoavoiddeadlockistolocksomehigherorderobjectthatisrelatedtothemanylowerorderobjectsweneedtouse.Inour
example,thatmeansremovingtheefficiencythatcausesthisdeadlock:touseonlyonelocktoprotectthescoreandthecharacterassignments.
Ofcourse,thisisonlyasimpleexample:wedon'tneedtolockeverything.Ifwecanisolatethelocationofthedeadlock,wecanuseaslightlyhigherorder
lockonlytoprotectthemethodsthatarehavingproblems.Orwecanmakearulethataddstherequirementthatanadditionallockbeheldpriortoacquiring
theproblemlocks.Allthesevariationsoflockingmultipleobjectssufferfromthesamelockgranularityproblemthatwe'reabouttodiscuss.
Theproblemwiththistechniqueisthatitoftenleadstosituationswherethelockgranularityisnotideal.Bysynchronizingwithonlyonelock,weare
preventingaccesstovariableswemaynotbechangingorevenusing.Thepurposeofthreadedprogrammingistoaccomplishtaskssimultaneouslynotto
havethesethreadswaitingonsomegloballock.Furthermore,ifwe'vedoneourprogramdesigncorrectly,therewasprobablyareasonwhyweattemptedto
acquiremultiplelocksratherthanasinglegloballock.Solvingdeadlockissuesbyviolatingthisdesignbecomessomewhatcounterproductive.
Themostpracticalruletoavoiddeadlockistomakesurethatthelocksarealwaysacquiredinthesameorder.Inourexample,itmeansthateitherthescoreor
characterlockmustbeacquiredfirstitdoesn'tmatterwhichaslongasweareconsistent.Thisimpliestheneedforalockhierarchymeaningthatlocksare
notonlyprotectingtheirindividualitemsbutarealsokeepinganordertotheitems.Thescorelockprotectsnotonlythevaluesofthescore,butthecharacter
lockaswell.ThisisthetechniquethatweusedtofixthedeadlockinChapter3:
packagejavathreads.examples.ch03.example9;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
publicvoidresetScore(){
try{
scoreLock.lock();
charLock.lock();
score=0;
char2type=1;
setScore();
}finally{
charLock.unlock();
scoreLock.unlock();
}
}
...
}
SincetheresetScore()methodnowalsograbsthescorelockfirst,itisnotpossibleforanythreadtobewaitingforthescorelockwhileholdingthe
characterlock.Thismeansthatthecharacterlockmayeventuallybegrabbedandreleased,followedbytheeventualreleaseofthescorelock.Adeadlockdoes
notoccur.
Again,thisisaverysimpleexample.Formuchmorecomplexsituations,wemayhavetodoallofthefollowing:
UseonlylockingobjectsthingsthatimplementtheLockinterfaceandavoiduseofthesynchronizedkeyword.Thisallowstheseparationofthe
locksfromtheobjectsintheapplication.Wedothisevenwithoursimpleexample.
Understandwhichlocksareassignedtowhichsubsystemsandunderstandtherelationshipsbetweenthesubsystems.Wedefineasubsystemasaclass,
groupofclasses,orlibrarythatperformsarelativelyindependentservice.Thesubsystemmusthaveadocumentedinterfacethatwecantestordebugin
oursearchfordeadlocks.Thisallowsustoformgroupsoflocksandmapoutpotentialdeadlocks.
Formalockinghierarchywithineachsubsystem.Unliketheothertwosteps,thiscanactuallyhurttheefficiencyoftheapplication.Thesubsystemneeds
tobestudied.Therelationshipofthelocksmustbeunderstoodinordertobeabletoformahierarchythatwillhaveminimalimpactontheefficiencyof
theapplication.
IfyouaredevelopingaverycomplexJavaprogram,it'sagoodideatodevelopalockhierarchywhentheapplicationisbeingdesigned.Itmaybevery
difficulttoenforcealockhierarchyaftermuchoftheprogramhasbeendeveloped.Finally,sincethereisnomechanismtoenforcealockhierarchy,itisupto
yourgoodprogrammingpracticestomakesurethatthelockhierarchyisfollowed.Followingalockacquisitionhierarchyisthebestwaytoguaranteethat
deadlockdoesnotoccurinyourJavaprogramduetosynchronization.
publicvoidresetScore(){
scoreLock.lock();
charLock.lock();
score=0;
char2type=1;
setScore();
charLock.unlock();
scoreLock.unlock();
}
However,whathappensifthethreadthatcallstheresetScore()methodencountersaruntimeexceptionandterminates?Undermanythreadingsystems,
thisleadstoatypeofdeadlockbecausethethreadthatterminatesdoesnotautomaticallyreleasethelocksitheld.Underthosesystems,anotherthreadcould
waitforeverwhenittriestochangethescore.InJava,however,locksassociatedwiththesynchronizedkeywordarealwaysreleasedwhenthethread
leavesthescopeofthesynchronizedblock,evenifitleavesthatscopeduetoanexception.SoinJavawhenusingthesynchronizedkeyword,thistypeof
deadlockneveroccurs.
ButweareusingtheLockinterfaceinsteadofthesynchronizedkeyword.ItisnotpossibleforJavatofigureoutthescopeoftheexplicitlockthe
developer'sintentmaybetoholdthelockevenonanexceptioncondition.Consequently,inthisnewversionoftheresetScore()method,ifthe
setScore()methodthrowsaruntimeexception,thelockisneverfreedsincetheunlock()methodsarenevercalled.
Thereisasimplewayaroundthis:wecanuseJava'sfinallyclausetomakesurethatthelocksarefreeduponcompletion,regardlessofhowthemethod
exits.Thisiswhatwe'vedoneinallourexamples.
Bytheway,thisantideadlockbehaviorofthesynchronizedkeywordisnotnecessarilyagoodthing.Whenathreadencountersaruntimeexceptionwhile
itisholdingalock,there'sthepossibilityindeed,theexpectationthatitwillleavethedataitwasmanipulatinginaninconsistentstate.Ifanotherthreadis
thenabletoacquirethelock,itmayencounterthisinconsistentdataandproceederroneously.
Whenusingexplicitlocks,youshouldnotonlyusethefinallyclausetofreethelock,butyoushouldalsotestfor,andcleanupafter,theruntimeexception
condition.Unfortunately,givenJava'ssemantics,thisproblemisimpossibletosolvecompletelywhenusingthesynchronizedkeywordorbyusingthe
finallyclause.Infact,it'sexactlythisproblemthatledtothedeprecationofthestop()method:thestop()methodworksbythrowinganexception,
whichhasthepotentialtoleavekeyresourcesintheJavavirtualmachineinaninconsistentstate.
Sincewecannotsolvethisproblemcompletely,itmaysometimesbebettertouseexplicitlocksandriskdeadlockifathreadexitsunexpectedly.Itmaybe
bettertohaveadeadlockedsystemthantohaveacorruptedsystem.
Deadlock Detection
Theproblemwithdeadlockisthatitcausestheprogramtohangindefinitely.Obviously,ifaprogramhangs,deadlockmaybethecause.Butisdeadlock
alwaysthecause?Inprogramsthatwaitforusers,waitforexternalsystems,orhavecomplexinteractions,itcanbeverydifficulttotelladeadlocksituation
fromthenormaloperationoftheprogram.Furthermore,whatifthedeadlockislocalized?Asmallgroupofthreadsintheprogrammaydeadlockwitheach
otherwhileotherthreadscontinuerunning,maskingthedeadlockfromtheuser(ortheprogramitself).Whileitisverydifficulttopreventdeadlock,canwe
atleastdetectit?Tounderstandhowtodetectdeadlock,wemustfirstunderstanditscause.
Figure61showstwocasesofthreadsandlockswaitingforeachother.Thefirstcaseisoflockswaitingfortheownerthreadtofreethem.Thelocksare
ownedbythethreadsotheycan'tbeusedbyanyotherthread.Anythreadthattriestoobtaintheselocksisplacedintoawaitstate.Thisalsomeansthatifthe
threaddeadlocks,itcanmakemanylocksunavailabletootherthreads.
Figure61.Locktrees
Thesecondcaseisofthreadswaitingtoobtainalock.Ifthelockisownedbyanotherthread,thethreadmustwaitforittobefree.Technically,thelockdoes
notownthethread,buttheeffectisthesamethethreadcan'taccomplishanyothertaskuntilthelockisfreed.Furthermore,alockcanhavemanythreads
waitingforittobefree.Thismeansthatifalockdeadlocks,itcanblockmanywaitingthreads.
Wehaveintroducedmanynewtermsherewe'llexplainthembeforewemoveon.Wedefineadeadlockedlockasalockthatisownedbyathreadthathas
deadlocked.Wedefineadeadlockedthreadasathreadthatiswaitingforadeadlockedlock.Thesetwodefinitionsareadmittedlycircular,butthatishow
deadlocksarecaused.Athreadownsalockthathaswaitingthreadsthatownalockthathaswaitingthreadsthatownalock,andsoon.Adeadlockoccursif
theoriginalthreadneedstowaitforanyoftheselocks.Ineffect,aloophasbeencreated.Wehavelockswaitingforthreadstofreethem,andthreadswaiting
forlockstobefreed.Neithercanhappenbecauseindirectlytheyarewaitingforeachother.
Wedefineahardwaitasathreadtryingtoacquirealockbywaitingindefinitely.Wecallitasoftwaitifatimeoutisassignedtothelockacquisition.The
timeoutistheexitstrategyifadeadlockoccurs.Therefore,fordeadlockdetectionsituations,weneedonlybeconcernedwithhardwaits.Interruptedwaitsare
interestinginthisregard.Ifthewaitcanbeinterrupted,isitahardwaitorasoftwait?Theanswerisnotsimplebecausethereisnowaytotellifthethread
thatisimplementedtocalltheinterrupt()methodatalatertimeisalsoinvolvedinthedeadlock.Fornow,wewillsimplynotallowthewaitforthelock
tobeinterrupted.Wewillrevisittheissueofthedelineationbetweensoftandhardwaitslaterinthischapter.However,inouropinion,interruptiblewaits
shouldbeconsideredhardwaitssinceusinginterruptsisnotcommoninmostprograms.
Assumingthatwecankeeptrackofallofthelocksthatareownedbyathreadandkeeptrackofallthethreadsthatareperformingahardwaitonalock,is
detectingapotentialdeadlockpossible?Yes.Figure62showsapotentialtreethatisformedbylocksthatareownedandformedbyhardwaitingthreads.
Givenathread,thisfigureshowsallthelocksthatareownedbyit,allthethreadsthatarehardwaitingonthoselocksinturn,andsoon.Ineffect,eachlockin
thediagramisalreadywaiting,whetherdirectlyorindirectly,fortherootthreadtoeventuallyallowittobefree.Ifthisthreadneedstoperformahardwaiton
alock,itcan'tbeonethatisinthistree.Doingsocreatesaloop,whichisanindicationofadeadlocksituation.Insummary,wecandetectadeadlockby
simplytraversingthistree.Ifthelockisalreadyinthistree,aloopisformed,andadeadlockconditionoccurs.
Figure62.Completedthreadwaittree
Usingthisalgorithm,hereisanimplementationofadeadlockdetectinglock:
packagejavathreads.examples.ch06;
publicclassDeadlockDetectedExceptionextendsRuntimeException{
publicDeadlockDetectedException(Strings){
super(s);
}
}
packagejavathreads.examples.ch06;
importjava.util.*;
importjava.util.concurrent.*;
importjava.util.concurrent.locks.*;
publicclassDeadlockDetectingLockextendsReentrantLock{
privatestaticListdeadlockLocksRegistry=newArrayList();
privatestaticsynchronizedvoidregisterLock(DeadlockDetectingLockddl){
if(!deadlockLocksRegistry.contains(ddl))
deadlockLocksRegistry.add(ddl);
}
privatestaticsynchronizedvoidunregisterLock(DeadlockDetectingLockddl){
if(deadlockLocksRegistry.contains(ddl))
deadlockLocksRegistry.remove(ddl);
}
privateListhardwaitingThreads=newArrayList();
privatestaticsynchronizedvoidmarkAsHardwait(Listl,Threadt){
if(!l.contains(t))l.add(t);
}
privatestaticsynchronizedvoidfreeIfHardwait(Listl,Threadt){
if(l.contains(t))l.remove(t);
}
privatestaticIteratorgetAllLocksOwned(Threadt){
DeadlockDetectingLockcurrent;
ArrayListresults=newArrayList();
Iteratoritr=deadlockLocksRegistry.iterator();
while(itr.hasNext()){
current=(DeadlockDetectingLock)itr.next();
if(current.getOwner()==t)results.add(current);
}
returnresults.iterator();
}
privatestaticIteratorgetAllThreadsHardwaiting(DeadlockDetectingLockl){
returnl.hardwaitingThreads.iterator();
}
privatestaticsynchronized
booleancanThreadWaitOnLock(Threadt,DeadlockDetectingLockl){
IteratorlocksOwned=getAllLocksOwned(t);
while(locksOwned.hasNext()){
DeadlockDetectingLockcurrent=
(DeadlockDetectingLock)locksOwned.next();
if(current==l)returnfalse;
IteratorwaitingThreads=getAllThreadsHardwaiting(current);
while(waitingThreads.hasNext()){
Threadotherthread=(Thread)waitingThreads.next();
if(!canThreadWaitOnLock(otherthread,l)){
returnfalse;
}
}
}
returntrue;
}
publicDeadlockDetectingLock(){
this(false,false);
}
publicDeadlockDetectingLock(booleanfair){
this(fair,false);
}
privatebooleandebugging;
publicDeadlockDetectingLock(booleanfair,booleandebug){
super(fair);
debugging=debug;
registerLock(this);
}
publicvoidlock(){
if(isHeldByCurrentThread()){
if(debugging)System.out.println("AlreadyOwnLock");
super.lock();
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
return;
}
markAsHardwait(hardwaitingThreads,Thread.currentThread());
if(canThreadWaitOnLock(Thread.currentThread(),this)){
if(debugging)System.out.println("WaitingForLock");
super.lock();
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
if(debugging)System.out.println("GotNewLock");
}else{
thrownewDeadlockDetectedException("DEADLOCK");
}
}
publicvoidlockInterruptibly()throwsInterruptedException{
lock();
}
publicclassDeadlockDetectingConditionimplementsCondition{
Conditionembedded;
protectedDeadlockDetectingCondition(ReentrantLocklock,Conditione){
embedded=e;
}
publicvoidawait()throwsInterruptedException{
try{
markAsHardwait(hardwaitingThreads,Thread.currentThread());
embedded.await();
}finally{
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
}
}
publicvoidawaitUninterruptibly(){
markAsHardwait(hardwaitingThreads,Thread.currentThread());
embedded.awaitUninterruptibly();
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
}
publiclongawaitNanos(longnanosTimeout)throwsInterruptedException{
try{
markAsHardwait(hardwaitingThreads,Thread.currentThread());
returnembedded.awaitNanos(nanosTimeout);
}finally{
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
}
}
publicbooleanawait(longtime,TimeUnitunit)
throwsInterruptedException{
try{
markAsHardwait(hardwaitingThreads,Thread.currentThread());
returnembedded.await(time,unit);
}finally{
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
}
}
publicbooleanawaitUntil(Datedeadline)throwsInterruptedException{
try{
markAsHardwait(hardwaitingThreads,Thread.currentThread());
returnembedded.awaitUntil(deadline);
}finally{
freeIfHardwait(hardwaitingThreads,Thread.currentThread());
}
}
publicvoidsignal(){
embedded.signal();
}
publicvoidsignalAll(){
embedded.signalAll();
}
}
publicConditionnewCondition(){
returnnewDeadlockDetectingCondition(this);
}
}
Beforewegointodetailonthisdeadlockdetectinglock,itmustbenotedthatthislistinghasbeencutdownforthisbook.Forthelatest,fullycommented
version,includingtestingtools,pleaseseetheonlineexamples,whichinclude(asexample1)aclassthatcanbeusedtotestthisimplementation.
Intermsofimplementation,thisclassinheritsfromtheLockinterface,soitmaybeusedanywherethataLockobjectisrequired.Furthermore,deadlock
detectionrequirestheregistrationofalllocksinvolvedinthedeadlock.Therefore,todetectadeadlock,replaceallthelockswiththisclass,eventhelocks
providedbythesynchronizedkeyword.Itmaynotbepossibletodetectaloopifanyofthelocksareunregistered.
Tousethisclass,replaceallinstancesofReentrantLockwithDeadlockDetectingLock.Thisslowsdownyourprogram,butwhenadeadlockis
detected,aDeadlockDetectedExceptionisimmediatelythrown.Becauseoftheperformanceimplicationsofthisclass,wedonotrecommendusingit
inaproductionenvironment:useitonlytodiagnoseoccurrencesofdeadlock.Theadvantageofusingthisclassisthatitdetectsthedeadlockimmediately
whenitoccursinsteadofwaitingforasymptomofthedeadlocktooccuranddiagnosingtheproblemthen.
TheDeadlockDetectingLockclassmaintainstwolistsadeadlockLocksRegistryandahardwaitingThreadslist.Bothoftheselistsare
storedinthreadunsafelistsbecauseexternalsynchronizationwillbeusedtoaccessthem.Inthiscase,theexternalsynchronizationistheclasslockall
accessestotheselistscomefromsynchronizedstaticmethods.AsingledeadlockLocksRegistrylistholdsalldeadlockdetectinglocksthathavebeen
created.OnehardwaitingThreadslistexistsforeachdeadlockdetectinglock.Thislistisnotstaticitholdsallthethreadobjectsthatareperforminga
hardwaitontheparticularlock.
ThedeadlocklocksareaddedandremovedfromtheregistrybyusingtheregisterLock()andunregisterLock()methods.Threadsareaddedand
removedfromthehardwaitinglistusingthemarkAsHardwait()andfreeIfHardwait()methodsrespectively.Sincethesemethodsarestaticwhile
thelistisnotthelistmustbepassedasoneoftheparameterstothesemethods.Intermsofimplementation,theyaresimpletheobjectsareaddedand
removedfromalistcontainer.
ThegetAllLocksOwned()andgetAllThreadsHardwaiting()methodsareusedtogetthetwotypesofwaitingsubtreeswementionedearlier.
Usingthesesubtrees,wecanbuildthecompletewaittreethatneedstobetraversed.ThegetAllThreadsHardwaiting()methodsimplyreturnsthelist
ofhardwaitingthreadsalreadymaintainedbythedeadlockdetectinglock.Thelistofownedlocksisslightlymoredifficult.ThegetAllLocksOwned()
methodhastotraverseallregistereddeadlockdetectinglocks,lookingforlocksthatareownedbythetargetthread.Intermsofsynchronization,bothofthese
methodsarecalledfromamethodthatownstheclasslockasaresult,thereisnoneedfortheseprivatemethodstobesynchronized.
ThecanThreadWaitOnLock()methodisusedtotraversethewaittree,lookingtoseeifaparticularlockisalreadyinthetree.Thisistheprimarymethod
thatisusedtodetectpotentialdeadlocks.Whenathreadisabouttoperformahardwaitonalock,itcallsthismethod.Adeadlockisdetectedifthelockis
alreadyinthewaittree.Notethattheimplementationisrecursive.Themethodexaminesallofthelocksownedtoseeifthelockisinthefirstlevelofthetree.
Italsotraverseseachownedlocktogetthehardwaitingthreadseachhardwaitingthreadisfurtherexaminedrecursively.Thismethodusestheclasslockfor
synchronization.
Withtheabilitytodetectdeadlocks,wecannowoverridethelock()methodoftheReentrantLockclass.Thisnewimplementationisactuallynotthat
simple.TheReentrantLockclassisincrediblyoptimizedmeaningitusesminimalsynchronization.Inthatregard,ournewlock()methodisalso
minimallysynchronized.
Thefirstpartofthelock()methodisfornestedlocks.Ifthelockisalreadyownedbythisthread,thereisnoreasontocheckfordeadlocks.Instead,wecan
justcalltheoriginallock()method.Thereisnoraceconditionforthiscase:onlytheownerthreadcansucceedinthetestfornestedlocksandcallthe
originallock()method.Andsincethereisnochancethattheownerofthelockwillchangeiftheowneristhecurrentlyexecutingthread,thereisnoneedto
worryaboutthepotentialraceconditionbetweentheisHeldByCurrentThread()andsuper.lock()methodcalls.
Thesecondpartofthelock()methodisusedtoobtainnewlocks.ItfirstchecksfordeadlocksbycallingthecanThreadWaitOnLock()method.Ifa
deadlockisdetected,aruntimeexceptionisthrown.Otherwise,thethreadisplacedonthehardwaitlistforthelock,andtheoriginallock()methodis
called.Obviously,araceconditionexistsheresincethelock()methodisnotsynchronized.Tosolvethis,thethreadisplacedonthehardwaitlistbeforethe
deadlockcheckisdone.Bysimplyreversingthetasks,itisnolongerpossibleforadeadlocktogoundetected.Infact,adeadlockmaybeactuallydetected
beforeithappensduetotheracecondition.
Thereisnoreasontooverridethelockmethodsthatacceptatimeoutsincethesearesoftlocks.Theinterruptiblelockrequestisdisabledbyroutingittothe
uninterruptibleversionofthelock()method.
Unfortunately,wearenotdoneyet.Conditionvariablescanalsofreeandreacquirethelockanddosoinafashionthatmakesourdeadlockdetectingclass
muchmorecomplex.Thereacquisitionofthelockisahardwaitsincetheawait()methodcan'treturnuntilthelockisacquired.Thismeansthatthe
await()methodneedstoreleasethelock,waitforthenotificationfromthesignal()methodtoarrive,checkforapotentialdeadlock,performahard
waitforthelock,andeventuallyreacquirethelock.
Ifyou'vealreadyexaminedthecode,you'llnoticethattheimplementationoftheawait()methodissimplerthanwejustdiscussed.Itdoesn'tevencheckfor
thedeadlock.Instead,itsimplyperformsahardwaitpriortowaitingforthesignal.Byperformingahardwaitbeforereleasingthelock,wekeepthethread
andlockconnected.Thismeansthatifalaterlockattemptismade,aloopcanstillbedetected,albeitbyadifferentroute.Furthermore,sinceitisnotpossible
tocauseadeadlocksimplybyusingconditionvariables,thereisnoneedtocheckfordeadlockontheconditionvariableside.Theconditionvariablejust
needstoallowthedeadlocktobedetectedfromthelock()methodside.Theconditionvariablealsomustplacethethreadonthehardwaitlistpriorto
releasingthelockduetoaraceconditionwiththelock()methoditispossibletomissdetectionofthedeadlockifthelockisreleasedfirst.
Atthispoint,wearesuremanyreadershavehugediagramsontheirdeskormaybeonthefloorwiththreadandlockscenariosdrawninpencil.Deadlock
detectionisaverycomplexsubject.Wehavetriedtopresentitassimplyaspossible,butwearesuremanyreaderswillnotbeconvincedthatthisclass
actuallyworkswithoutafewhoursofplayingoutdifferentscenarios.Tohelpwiththis,thelatestonlinecopyofthisclasscontainsmanysimpletestcase
scenarios(whichcaneasilybeextended).
Tohelpfurther,hereareanswerstosomepossiblequestions.Ifyouarenotconcernedwiththesequestions,feelfreetoskiporskimthenextsectionas
desired.Asawarning,someofthesequestionsareveryobscure,soobscurethatsomequestionsmaynotevenbeunderstoodwithoutafewhoursofpaper
andpencilwork.Thegoalistoworkoutthescenariostounderstandthequestions,whichcanhopefullybeansweredhere.
Wehavestatedthatadeadlockconditionisdetectedwhenaloopinthewaittreeisdetected.Isitreallyaloop?Theanswerisyes.Thismeansthatwehaveto
becarefulinoursearchorwecanrecursivelysearchforever.Let'sexaminehowtheloopisformedfromanotherpointofview.Anywaitingthreadnodecan
haveonlyoneparentlocknode.That'sbecauseathreadcan'tperformahardwaitonmorethanonelockatatime.Anyownedlocknodecanhaveonlyone
parentthreadnode.That'sbecausealockcan'tbeownedbymorethanonethreadatatime.Inthisdirection,onlynodesconnectedtothetopnodecanform
theloop.Aslongasnoneoftheownedlocknodesareconnectedtothetopthreadnode,wedon'thavealoop.Itisslightlymorecomplicatedthanthis,butwe
willaddressitwiththenextquestion.
Whyareweusingonlythethreadtree?Whataboutthelocktree?Thesequestionsintroduceacoupleofdefinitions,solet'sbackupafewsteps.Tobegin,we
aretryingtodeterminewhetherathreadcanperformahardwaitonaparticularlock.Wethenbuildawaittreeusingthisthreadobjectasthetopnodethat's
whatwemeanbythethreadtree.However,thelockisn'tindependent.Itisalsopossibletobuildawaittreeusingthelockobjectasthetopnode,whichwe
defineasthelocktree.Theremaybeotherlocksinthelocktreethatcouldbeinthethreadtree,possiblyformingadeadlockcondition.
Fortunately,wedon'thavetotraversethelocktreebecausethethreadtreeisguaranteedtocontainarootnodeasthetopnode.Thetopnodeofthethreadtree
isthecurrentlyrunningthread.Itisnotpossibleforthisthreadtobecurrentlywaitingonalocksinceitwouldn'tbeexecutingthelockrequest.Thetopnode
ofthelocktreeisonlytherootnodeifthelockisnotowned.Foralooptoform,eitherthelocktreeorthethreadtreemustbeasubtreeoftheother.Sincewe
knowthatthethreadtreedefinitelycontainstherootnode,onlythelocknodecanbethesubtree.Totestforasubtree,wejustneedtotestthetopnode.
Isn'tmarkingthehardwaitpriortocheckingforthedeadlockconditionaproblem?Canitcausespuriousdeadlockexceptions?Theanswerisno.The
deadlockconditionwilldefinitelyoccursincethethreadwilleventuallyperformthehardwait.Itisjustbeingdetectedslightlybeforeitactuallyhappens.On
theotherhand,ourclassmaythrowmorethanonedeadlockexceptiononcethedeadlockhasbeendetected.Itmustbenotedthatthepurposeofthisclassis
nottorecoverfromthedeadlock.Infact,onceadeadlockexceptionisthrown,theclassdoesnotcleanupafterit.Aretryattemptthrowsthesameexception.
Canmarkingthehardwaitfirstinterferewiththedeadlockcheck?Bymarkingfirst,wearemakingaconnectionbetweenthethreadandthelock.Intheory,
thisconnectionshouldbedetectedasadeadlockconditionbythedeadlockcheck.Todetermineifwe'reinterferingwiththedeadlockcheck,wehaveto
examinewheretheconnectionismade.Weareconnectingthelocknodetothetopthreadnodetheconnectionisactuallyabovethetopthreadnode.Since
thesearchstartsfromthetopthreadnode,itisn'tabletodetectthedeadlockunlessthelocknodecanbereachedfirst.Thisconnectionisseenfromthelock
treebutisnotaproblembecausethattreeisnottraversed.Traversalsbyotherthreadswillbedetectedearlyasadeadlockconditionsincethehardwaitwill
eventuallybeperformed.
Canmarkingthehardwaitfirstcauseanerrorconditioninotherthreads?Willitcausealoopinthetrees?Weneedtoavoidaloopinthewaittreesfortwo
reasons.First,andobviously,isbecauseitisanindicationofadeadlockcondition.Thesecondreasonisbecausewewillbesearchingthroughthewaittrees.
Recursivelysearchingthroughatreethathasaloopcausesaninfinitesearch(ifthelockbeingsoughtisnotwithintheloop).
Theanswertothisquestionisno,itcan'tcauseanerrorcondition.First,thereisnowaytoentertheloopfromathreadnodethatisnotwithintheloop.All
threadnodeswithintheloopareperformingahardwaitonlockswithintheloop.Andalllocknodeswithintheloopareownedbythreadnodeswithinthe
loop.Second,itisnotpossibletostartfromathreadnodethatiswithintheloop.Withtheexceptionofthetopthreadnode,allthethreadnodesare
performingahardwait.Tobeabletoperformthedeadlockcheck,athreadcannotbeinawaitstateandthereforecan'tbeinthewaittree.Ifaloopisformed,
onlythethreadrepresentedbythetopthreadnodecandetectthedeadlock.
Thisanswerassumesthatadeadlockdetectedexceptionhasneverbeenthrownthisclassisnotdesignedtoworkoncesuchanexceptionisthrown.Forthat
functionality,considerusingthealternatedeadlockdetectingclassthatisavailableonline.
Howcanthesimplesolutionofswitchingthe"threadownsthelock"tothe"threadhardwaitingforlock"workforconditionvariables?Admittedly,wedida
bitofhandwavingintheexplanation.Abetterwaytoenvisionitistotreattheoperationsasbeingseparateentitiesasiftheconditionvariableisreleasing
andreacquiringthelock.Sincethereacquisitionismandatory(i.e.,itwilleventuallyoccur),wemarkthethreadforreacquisitionbeforewereleasethelock.
Wecanarguethatswitchingtheownershipstatetoahardwaitstateremovestheconnectionfromthethreadtree,makingdetectionimpossible.Thisisjustan
artifactofexaminingthewaittreefromtheconditionvariable'sperspective.Whenthelock()methodiscalledatalatertime,wewillbeusingadifferent
threadobjectasthetopnode,formingadifferentwaittree.Fromthatperspective,wecanuseeithertheownershipstateorhardwaitstateforthedetectionof
thedeadlock.
Whydon'twehavetocheckforpotentialdeadlocksontheconditionvariableside?Itisnotnecessary.Markingforthewaitoperationpriortounlocking
worksinapseudoatomicmanner,meaningthatitisnotpossibleforanotherthreadtomissthedetectionofthedeadlockwhenusingthelock()method.
Sinceitisnotpossibletocreateanewdeadlockjustbyusingconditionvariables,wedon'tneedtocheckonthisend.Anotherexplanationisthatthereisno
needtocheckbecausewealreadyknowtheanswer:thethreadiscapableofperformingahardwaitbecauseithaspreviouslyownedthelockandhasnothad
achancetorequestadditionallocks.
Isn'tmarkingforthehardwaitpriortoperformingtheawait()operationaproblem?Canitcausespuriousdeadlockexceptions?Canitcauseanerror
conditioninotherthreads?Twoofthesequestionsareverysimilartothequestionsforthelock()methodside.Theextraquestionhereaddressestheissue
ofinterferingwiththedeadlockcheck.Thatquestiondoesn'tapplyonthelock()methodsidebecausewedonotperformadeadlockcheckonthecondition
variableside.
However,theanswerstotheotherquestionsarenotexactlythesameasbefore.Inthiscase,thethreadisperformingahardwaitonthelockbeforethethread
releasesownershipofthelock.Wearecreatingatemporaryloopaloopthatiscreatedeventhoughthedeadlockconditiondoesnotexist.Thisisnotacase
ofdetectingthedeadlockearlyiftheloopweredetected,thedeadlockdetectedwouldbeincorrect.
Thesethreequestionscanbeansweredtogether.Aswiththeerrorquestiononthelock()methodside,itisnotpossibletoentertheloopfromathreadnode
outsideoftheloop.Second,theonethreadnodethatiswithinthissmallloopisnotperformingadeadlockcheck.Andfinally,anydeadlockcheckdoesnot
traversethelocktree.Thismeansthatanerrorconditioncan'toccurinanotherthreadandthatdetectingafalsedeadlockconditionalsocan'toccurinanother
thread.Ofcourse,eventuallyitwouldbepossibletogettothelocknodeexternally,butbythen,theloopwouldhavebeenbroken.Itisnotpossiblefor
anotherthreadtoownthelockunlesstheconditionvariablethreadreleasesitfirst.
Toreview,wearetraversingthethreadtreetocheckwhetherthelocktreeisasubtree.Insteadofrecursivelytraversingfromthethreadtree,isn'titeasierto
traverseupwardfromthelocktree?Ouranswerismaybe.Wesimplylisttheplusesandminusesandletthereaderdecide.Twogoodpointscanbemadefor
traversingfromthelocktree.First,thesearchisnotrecursive.Eachnodeofthelocktreehasonlyoneparent,sogoingupwardcanbedoneiteratively.
Second,movingupwardfromlocknodetoparentthreadnodedoesnotneedanyiterationstheownerthreadobjectisalreadyreferencedbythelockobject.
Ontheotherhand,movingdownwardfromthethreadnodetothelocknoderequiresiterationthroughtheregisteredlockslist.
Unfortunately,therearetwobadpointstotraversingupwardfromthelocktree.First,movingupwardfromthethreadnodetothelocknodeonwhichitis
performingthehardwaitisincrediblytimeconsuming.Weneedtoiteratethroughtheregisteredlockslisttofindthehardwaitlists,whichwemust,inturn,
iteratethroughtofindthelocknode.Incomparison,movingdownwardfromthelocknodetothethreadnodeisdonebyiteratingthroughonehardwaitlist.
Anditgetsworse.Weneedtoiteratethroughallofthehardwaitlists.Bycomparison,weneedtoiterateonlythroughthehardwaitlistsinthewaittreeinour
existingimplementation.Thisonepointalonemayoutweighthegoodpoints.
Thesecondbadpointstemsfromthetechniquesthatweusetosolvetheraceconditionsinthelockclass.Theclassallowsloopstooccureventemporarily
creatingthemwhenadeadlockconditiondoesnotexist.Searchingfromalocknodethatiswithinaloopwhetherrecursivelydownwardoriteratively
upwarddoesnotterminateifthetopthreadnodeisnotwithintheloop.Fortunately,thisproblemcanbeeasilysolved.Wejustneedtoterminatethesearch
ifthetoplocknodeisfound.Alsonotethatfindingthetoplocknodeisnotanindicationofadeadlockconditionsincesometemporaryloopsareformed
evenwithoutadeadlockcondition.
Toreview,wearetraversingthethreadtreeinsteadofthelocktreebecausethetopthreadnodeisdefinitelytherootnode.Thetoplocknodemaynotbethe
rootnode.However,whatifthetoplocknodeisalsotherootnode?Isn'tthisashortcutinthesearchforadeadlock?Yes.Itisnotpossibleforthelocktree
tobeasubtreeofthethreadtreeifthetoplocknodeisarootnode.Thismeanswecanremovesomecallstothedeadlockcheckbyfirstcheckingtoseeifthe
lockisalreadyowned.Thisisanimportantimprovementsincethedeadlockcheckisverytimeconsuming.
However,araceconditionexistswhenalockhasnoowner.Ifthelockisunowned,thereisnoguaranteethatthelockwillremainunownedduringthe
deadlockcheck.Thisraceconditionisnotaproblemsinceitisnotpossibleforanylockinthewaittreetobeunownedatanytimeduringthedeadlockcheck
thedeadlockcheckmaybeskippedwhetherornotthelockremainsunowned.
Thisshortcutismostlyforlocksthatareinfrequentlyused.Forfrequentlyusedlocks,thisshortcutishighlydependentonthethreadfindingthelocktobe
free,whichisbasedonthetimingoftheapplication.
Themodificationwithsomedeadlockcheckingremovedisavailableonlineinouralternatedeadlockdetectinglock.
Thedeadlockdetectinglockdisallowsinterruptiblelockingrequests.Whatifwedonotagreewiththiscompromise?Thereareonlyafewoptions.
Disallowingtheinterruptwastheeasiestcompromisethatworksforthemajorityofthecases.Forthosereaderswhobelieveaninterruptiblelockshouldbe
consideredasoftlock,thechangeissimplejustdon'toverridethelockInterruptibly()method.Andforthosereaderswhobelievethatan
interruptiblelockshouldbeconsideredahardlockwhilestillnotcompromisinginterruptcapability,hereisamodifiedversionofthemethod:
publicvoidlockInterruptibly()throwsInterruptedException{
if(isHeldByCurrentThread()){
if(debugging)System.out.println("AlreadyOwnLock");
try{
super.lockInterruptibly();
}finally{
freeIfHardwait(hardwaitingThreads,
Thread.currentThread());
}
return;
}
markAsHardwait(hardwaitingThreads,Thread.currentThread());
if(canThreadWaitOnLock(Thread.currentThread(),this)){
if(debugging)System.out.println("WaitingForLock");
try{
super.lockInterruptibly();
}finally{
freeIfHardwait(hardwaitingThreads,
Thread.currentThread());
}
if(debugging)System.out.println("GotNewLock");
}else{
thrownewDeadlockDetectedException("DEADLOCK");
}
}
Thischangeisalsoprovidedonlineinouralternatedeadlockdetectinglockclass.Intermsofimplementation,itispracticallyidenticaltothatofthelock()
method.Thedifferenceisthatwenowplacealllockrequestswithinatryfinallyclause.Thisallowsthemethodtocleanupaftertherequest,regardless
ofwhetheritexitsnormallyorbyexception.
Thedeadlockdetectinglockregardsalllockrequestswithatimeoutassoftlocks.Whatifwedonotagreewiththispremise?Thistopicisopentodebate.
Whileanapplicationthatusestimeoutsshouldhaveanexitstrategywhenthetimeoutoccurs,whatiftheexitstrategyistoinformtheuserandthensimply
retry?Inthiscase,deadlockcouldoccur.Furthermore,atwhatpointisretryingnolongertolerable?Whenthetimeoutperiodismorethananhour?Aday?A
month?Obviously,theseissuesaredesignspecific.
HereisanimplementationofthetryLock()methodthattreatstherequestasasoftwaitbutonlyifitislessthanaminute:
publicbooleantryLock(longtime,TimeUnitunit)
throwsInterruptedException{
//Performoperationasasoftwait
if(unit.toSeconds(time)<60){
returnsuper.tryLock(time,unit);
}
if(isHeldByCurrentThread()){
if(debugging)System.out.println("AlreadyOwnLock");
try{
returnsuper.tryLock(time,unit);
}finally{
freeIfHardwait(hardwaitingThreads,
Thread.currentThread());
}
}
markAsHardwait(hardwaitingThreads,Thread.currentThread());
if(canThreadWaitOnLock(Thread.currentThread(),this)){
if(debugging)System.out.println("WaitingForLock");
try{
returnsuper.tryLock(time,unit);
}finally{
freeIfHardwait(hardwaitingThreads,
Thread.currentThread());
if(debugging)System.out.println("GotNewLock");
}
}else{
thrownewDeadlockDetectedException("DEADLOCK");
}
}
Thischangeisalsoprovidedintheonlineexamplesasanalternativetothedeadlockdetectinglockclass(includingatestingprogram,whichisexample2in
thischapter).Itsimplementationispracticallyidenticaltothatofthelock()method.Again,thedifferenceisthatwenowplacealllockrequestswithina
tryfinallyclause.Thisallowsthemethodtocleanupaftertherequest,regardlessifitexitsnormallyorbyexception.Thisexampletreatstheoperation
asasoftwaitforrequeststhatareunderaminute.Therequestistreatedasahardwaitotherwise.Weleaveituptoyoutomodifythecodetosuityourneeds.
Onealternatesolutioncouldbetouseadifferenttimeperiodtoseparatesoftandhardwaitlockoperationsthistimeperiodcouldalsobecalculateddepending
onconditionsintheprogram.Anotheralternatesolutioncouldbeforthetrylock()methodtoreturnfalseinsteadofthrowingthedeadlockdetected
exception.
Whilethedeadlockdetectinglockiswelldesignedfordetectingthedeadlockcondition,thedesignforreportingtheconditionisprettyweak.Aretherebetter
options?Thisisactuallyintentional.Thisclassisnotdesignedtobeusedinaproductionenvironment.Searchingfordeadlockscanbeveryinefficientthis
classshouldbeusedonlyduringdevelopment.Infact,mostreaderswillprobablynotusethisclassatall.Themainpurposeofthisclassissothatwecan
understanddeadlockshowtodetectthemand,eventually,howtopreventthem.
Forthosewhoinsistonusingthedeadlockdetectinglockinaproductionenvironment,thereareafewoptions.Theclasscanbedesignedtofailfast
meaningthatifadeadlockisdetected,theclasscouldthrowtheexceptionforeveryinvocation,regardlessofwhethertherequestisinvolvedinthedeadlock
ornot.Anotheroptionisfortheclasstoreporttheconditioninamannerthatallowstheprogramtoshutdownproperly.Athird,andnotrecommended,
optionistoallowtheclasstocontinuefunctioning.Thefirstandthirdoptionsareprovidedasconditionalcodeinthealternateonlineexample.
Thistopicofdeadlockdetectionseemstobeincrediblycomplex.Infact,thediscussiononthetheoryandimplementationismorethantwiceaslongasthe
codeitself.Isthistopicreallythatcomplex?Theconceptofdeadlockdetectioniscomplex,butthereisanotherreasonwhythisclassisevenmorecomplex.
Theimplementationofthisclassisaccomplishedbyminimalsynchronization.ThisismainlybecausetheReentrantLockclassisimplementedwith
minimalsynchronization,makingtheclassimplementationmorecomplex.
Lock Starvation
Whenevermultiplethreadscompeteforascarceresource,thereisthedangerofstarvation,asituationinwhichthethreadnevergetstheresource.InChapter
9,wediscusstheconceptinthecontextofCPUstarvation:withabadchoiceofschedulingoptions,somethreadsneverhavetheopportunitytobecomethe
currentlyrunningthreadandsufferfromCPUstarvation.
LockstarvationissimilartoCPUstarvationinthatthethreadisunabletoexecute.ItisdifferentfromCPUstarvationinthatthethreadisgiventhe
opportunitytoexecuteitisjustnotabletobecauseitisunabletoobtainthelock.Lockstarvationissimilartoadeadlockinthatthethreadwaitsindefinitely
foralock.Itisdifferentinthatitisnotcausedbyaloopinthewaittree.Itsoccurrenceissomewhatrareandiscausedbyaverycomplexsetof
circumstances.
Lockstarvationoccurswhenaparticularthreadattemptstoacquirealockandneversucceedsbecauseanotherthreadisalreadyholdingthelock.Clearly,this
canoccuronasimplebasisifonethreadacquiresthelockandneverreleasesit:allotherthreadsthatattempttoacquirethelockneversucceedandstarve.
Lockstarvationcanalsobemoresubtleifsixthreadsarecompetingforthesamelock,it'spossiblethatfivethreadswillholdthelockfor20%ofthetime,
thusstarvingthesixththread.
LockstarvationisnotsomethingmostthreadedJavaprogramsneedtoconsider.IfourJavaprogramisproducingaresultinafiniteperiodoftime,
eventuallyallthreadsintheprogramwillacquirethelock,ifonlybecausealltheotherthreadsintheprogramhaveexited.Lockstarvationalsoinvolvesthe
questionoffairness:atcertaintimeswewanttomakesurethatthreadsacquirethelocksinareasonableordersothatonethreadwon'thavetowaitforall
otherthreadstoexitbeforeithasitschancetoacquirethelock.
Considerthecaseoftwothreadscompetingforalock.AssumethatthreadAacquirestheobjectlockonafairlyperiodicbasis,asshowninFigure63.
Figure63.Callgraphofsynchronizedmethods
Here'swhathappensatvariouspointsonthegraph:
T0
AttimeT0,boththreadAandthreadBareabletorun,andthreadAisthecurrentlyrunningthread.
T1
ThreadAisstillthecurrentlyrunningthread,anditacquirestheobjectlockwhenitentersthesynchronizedblock.
T2
AtimesliceoccursthiscausesthreadBtobecomethecurrentlyrunningthread.
T3
Verysoonafterbecomingthecurrentlyrunningthread,threadBattemptstoenterthesynchronizedblock.ThiscausesthreadBtoblock.Thatallows
threadAtocontinuetorunthreadAcontinuesexecutinginthesynchronizedblock.
T4
ThreadAexitsthesynchronizedblock.ThreadBcouldobtainthelocknow,butitisstillnotrunningonanyCPU.
T5
ThreadAonceagainentersthesynchronizedblockandacquiresthelock.
T6
ThreadBonceagainisassignedtoaCPU.Itimmediatelytriestoenterthesynchronizedblock,butthelockforthesynchronizedblockisonceagainheld
bythreadA.So,threadBblocksagain.ThreadAthengetstheCPU,andwe'renowinthesamestateaswewereattimeT3.
It'spossibleforthiscycletocontinueforeversuchthatthreadBcanneveracquirethelockandactuallydousefulwork.
Clearly,thisexampleisapathologicalcase:CPUschedulingmustoccuronlyduringthosetimeperiodswhenthreadAholdsthelockforthesynchronized
block.Withtwothreads,that'sextremelyunlikelyandgenerallyindicatesthatthreadAisholdingthelockalmostcontinuously.Withseveralthreads,
however,it'snotoutofthequestionthatonethreadmayfindthateverytimeitisscheduled,anotherthreadholdsthelockitwants.
Synchronizedblockswithinloopsoftenhavethisproblem:
while(true){
synchronized(this){
//executesomecode
}
}
Atfirstglance,wemightexpectthisnottobeaproblemotherthreadscan'tstarvebecausethelockisfreedoften,witheachiterationoftheloop.Butaswe've
seen,thisisnotthecase:unlessanotherthreadrunsduringtheshortintervalbetweentheendofthesynchronizedblock(whenthelockisreleased)andthe
beginningofthenextiterationoftheloop(whenthelockisreacquired),nootherthreadwillbeabletoacquirethelock.
Therearetwopointstotakeawayfromthis:
Acquisitionoflocksdoesnotqueue
Whenathreadattemptstoacquirealock,itdoesnotchecktoseeifanotherthreadisalreadyattemptingtoacquirethelock(or,moreprecisely,ifanother
threadhastriedtoacquirethelockandblockedbecauseitwasalreadyheld).Inpseudocode,theprocesslookslikethis:
while(lockisheld)
waitforawhile
acquirelock
Forthreadsofequalpriority,there'snothinginthisprocessthatpreventsalockfrombeinggrantedtoonethreadevenifanotherthreadiswaiting.
Releasingalockdoesnotaffectthreadscheduling
Whenalockisreleased,anythreadsthatwereblockedwaitingforthatlockcouldrun.However,noactualschedulingoccurs,sononeofthethreadsthat
havejustmovedintotherunnablestateareassignedtotheCPUthethreadthathasjustreleasedthelockkeepsaccesstotheCPU.Thiscanbedifferentif
thethreadshavedifferentprioritiesorareonamultiprocessormachine(whereadifferentCPUmightbeidle).
Nonetheless,lockstarvationremains,asmightbeguessedfromourexample,somethingthatoccursonlyinrarecircumstances.Infact,eachofthe
followingcircumstancesmustbepresentforlockstarvationtooccur:
Multiplethreadsarecompetingforthesamelock
Thislockbecomesthescarceresourceforwhichsomethreadsmaystarve.
Theresultsthatoccurduringthisperiodofcontentionmustbeinterestingtous
If,forexample,we'recalculatingabigmatrix,there'sprobablyapointintimeatthebeginningofourcalculationduringwhichmultiplethreadsare
competingforthesamelockandCPU.Sinceallwecareaboutisthefinalresultofthiscalculation,itdoesn'tmattertousthatsomethreadsare
temporarilystarvedforthelock:westillgetthefinalanswerinthesameamountoftime.We'reconcernedaboutlockstarvationonlyifthere'saperiodof
timeduringwhichitmatterswhetherthelockisallocatedfairly.
Allofthepropertiesoflockstarvationstemfromthefactthatathreadattemptingtoacquirealockchecksonlytoseeifanotherthreadisholdingthelock
thethreadknowsnothingaboutotherthreadsthatarealsowaitingforthelock.Thisbehaviorinconjunctionwithpropertiesoftheprogramsuchasthe
numberofthreads,theirpriorities,andhowtheyarescheduledmanifestsitselfasacaseoflockstarvation.
Fortunately,thisproblemhasalreadybeensolvedbytheReentrantLockclass.Ifwe'reinoneoftheraresituationswherelockstarvationcanoccur,we
justneedtoconstructaReentrantLockobjectwherethefairnessflagissettotrue.SincetheReentrantLockclasswithitsfairnessflagsetgrantsthe
lockonveryclosetoafirstcome,firstservedbasis,itisnotpossibleforanythreadtobestarvedforalockregardlessofthenumberofthreadsorhow
they'rewritten.
Unfortunately,thedownsidetousingtheReentrantLockclassinthismanneristhatweareaffectingthescheduling.Wediscusshowthreadsare
scheduledinChapter9,butingeneral,threadshaveapriority,andthehigherprioritythreadsaregiventheCPUmoreoftenthanlowprioritythreads.
However,theReentrantLockclassdoesnottakethatintoaccountwhenissuinglocks:locksareissuedfirstcome,firstservedregardlessofthethread's
priority.
Programsthatsetthreadprioritiesdosoforareason.Thereasoncouldbebecausethedeveloperwantedtohavetheschedulerbehaveinacertainmanner.
WhileusingthefairflagintheReentrantLockclassmaypreventlockstarvation,itmayalsochangethedesiredschedulingbehavior.
Lockstarvationisarareproblemithappensonlyunderverydistinctcircumstances.WhileitcanbeeasilyfixedwiththeReentrantLockclass,itmayalso
changesomeofthesedesiredcircumstances.Ontheotherhand,ifprioritiesandschedulingarenotaconcern,theReentrantLockclassprovidesavery
simpleandquickfix.
Summary
ThestrongintegrationoflocksintotheJavalanguageandAPIisveryusefulforprogrammingwithJavathreads.Javaalsoprovidesverystrongtoolsto
allowthreadprogrammingatahigherlevel.Withthesetools,Javaprovidesacomprehensivelibrarytouseforyourmultithreadedprograms.
Evenwiththislibrary,threadedprogrammingisnoteasy.Thedeveloperneedstounderstandtheissuesofdeadlockandstarvation,inordertodesign
applicationsinaconcurrentfashion.Whileitisnotpossibletohaveaprogramthreadedautomaticallywithacombinationofusingthemoreadvancedtools
anddevelopmentpractices,itcanbeveryeasytodesignanddebugthreadedprograms.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
DeadlockdetectingLock
javathreads.examples.ch06.example1.DeadlockDetectingLock
ch6ex1
AlternateDeadlockdetectingLock
javathreads.examples.ch06.example2.AlternateDeadlockDetectingLock
ch6ex2
Threetestsareavailableforeachexample.Thefirsttestusestwothreadsandtwocompetinglocks.Thesecondtestusesthreethreadsandthreecompeting
locks.Thethirdtestusesconditionvariablestocausethedeadlock.Testnumbersareselectedwiththisproperty:
<propertyname="DeadlockTestNumber"value="2"/>
CharacterDisplayCanvas()
CharacterDisplayCanvas.preferredSize()
CharacterDisplayCanvas.newCharacter()
CharacterDisplayCanvas.paintComponent()
SwingTypeTester.initComponents()
TheactionPerformed()methodsoftheSwingTypeTesterbuttonobjects
ThekeyPressed()methodoftheSwingTypeTestercanvas
ScoreLabel.setScore()
AnimatedCharacterDisplayCanvas()
AnimatedCharacterDisplayCanvas.newCharacter()
AnimatedCharacterDisplayCanvas.paintComponent()
TowriteathreadsafeSwingprogram,wemustmakesurethatthemethodslistedaboveareaccessedonlyfromwithintheeventdispatchingthread.Notethat
thislistincludestheconstructorfortheAnimatedCharacterDisplayCanvasclassrememberthattheconstructorcallstheconstructorofitssuperclass.
TheSwingclasseshavealreadymadesurethatallcallbacksoccurontheeventdispatchingthread.ThepreferredSize(),paintComponent(),
keyPressed(),andactionPerformed()methodsareallcallbacks,sowedon'tneedtoworryaboutthose.TheinitComponents()methodiscalled
fromthemainthreadoftheprogram,whichisnottheeventdispatchingthread.Theconstructorforthedisplaycanvasesiscalledfromthesamethread.
However,theinitComponents()methodanditsconstructorscreatetheSwingobjectstheyhavenotyetbeendisplayed.Thatfallsintothefirstexception
casethatwelistedearlier.ThenewCharacter()methodcallsonlytherepaint()method,sothatfallsintothesecondexceptionwelistedabove.Finally,
thesetScore()methodaccessesSwingcomponentsonlywithintheinvokeLater()method,sothatfallsintoourthirdcategory.AllaccesstoSwing
classeswithinourapplicationishandledcorrectly.
Thefirsttwoexceptionsinourlistareselfexplanatory.Inthenextsection,weexplainthelasttwoexceptionsinourlist.
TheinvokeLater()andinvokeAndWait()methodsallowyoutodefineataskandasktheeventprocessingthreadtoperformthattask.Ifyouhavea
nonGUIthreadthatneedstoreadthevalueofaslider,forinstance,youputthecodetoreadthesliderintoaRunnableobjectandpassthatRunnable
objecttotheinvokeAndWait()method,whichreturnsthevaluethethreadneedstoread.
Let'slookagainatourscorelabelclass.ThesetScore()methodofthatclasscanbecalledwhentheusertypesacharacter(inwhichcaseitisrunningon
theeventdispatchingthread).Itcanalsobecalledwhentherandomcharactergeneratorsendsanewcharacter.Therefore,thesetScore()methodmustuse
theinvokeLater()methodtomakethatcall:
packagejavathreads.examples.ch07.example1;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
privatevoidsetScore(){
SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
setText(Integer.toString(score));
}
});
}
}
TheinvokeLater()methodtakesaRunnableobjectasitsparameter.Itsendsthatobjecttotheeventdispatchingthread,whichexecutestherun()
method.Thisiswhyit'salwayssafefortherun()methodtoexecuteSwingcode.
Notethattherun()methodisinitsownobject.Thisiswhywemadethescorevariablevolatileratherthanprotectingitbyusingsynchronization.
Synchronizingtherun()methodgrabsthelockoftheanonymousinnerclassobject,notthelockoftheScoreLabelobject.It'smucheasiertousea
volatilevariable.
Forthemostpart,theinvokeAndWait()methodlookssimilar,butithasthreeimportantsemanticdifferences.First,theinvokeLater()methodruns
asynchronouslyatsometimeinthefuture.Youdon'tknowwhenitwillactuallyrun.Ontheotherhand,theinvokeAndWait()methodissynchronous:it
doesnotreturnuntilitstargethascompletedexecution.Asaruleofthumb,then,youshouldusetheinvokeAndWait()methodtoreadthevalueofSwing
componentsortoensurethatsomethingisdisplayedonthescreenbeforeyoucontinueprogramexecution.Otherwise,youcanusetheinvokeLater()
method.
TheseconddifferenceisthattheinvokeAndWait()methodcannotitselfbecalledfromtheeventdispatchingthread.Thethreadrunningthe
invokeAndWait()methodmustwaitfortheeventdispatchingthreadtoexecutesomecode.Nothread,includingtheeventdispatchingthread,canwaitfor
itselftodosomethingelse.Consequently,ifyouexecutetheinvokeAndWait()methodfromtheeventdispatchingthread,itthrowsa
java.lang.Error.Thatcausestheeventdispatchingthreadtoexit(unlessyou'vetakentheunusualstepofcatchingErrorobjectsinyourcode)inturn,
yourentireprogrambecomesdisabled.
ThethirddifferenceisthattheinvokeAndWait()methodcanthrowanInterruptedExceptionifthethreadisinterruptedbeforetheevent
dispatchingthreadrunsthetarget,oranInvocationTargetExceptioniftheRunnableobjectthrowsaruntimeexceptionorerror.
Ifyouhavecodethatyouwanttotakeeffectimmediatelyandthatmightbecalledfromtheeventdispatchingthread,youcanusethe
SwingUtilities.isEventDispatchThread()methodtocheckthethreadyourcodeisexecutingon.YoucantheneithercallinvokeAndWait()(if
you'renotontheeventdispatchingthread)orcalltheSwingmethodsdirectly.
WecouldusethatmethodinourScoreLabelclasslikethis:
packagejavathreads.examples.ch07.example2;
...
publicclassScoreLabelextendsJLabelimplementsCharacterListener{
...
privatevoidsetScore(){
if(SwingUtilities.isEventDispatchThread())
setText(Integer.toString(score));
elsetry{
SwingUtilities.invokeAndWait(newRunnable(){
publicvoidrun(){
setText(Integer.toString(score));
}
});
}catch(InterruptedExceptionie){
}catch(InvocationTargetExceptionite){}
}
importjava.lang.reflect.*;
importjava.awt.*;
importjava.awt.event.*;
importjavax.swing.*;
publicclassFeedbackFrameextendsJFrameimplementsRunnable{
privateSwingTypeTesterstt;
privateThreadt;
privateJLabellabel;
privateintstate;
staticString[]stateMessages={
"Connectingtoserver...",
"Loggingintoserver...",
"Waitingfordata...",
"Complete"
};
publicFeedbackFrame(SwingTypeTesterstt){
this.stt=stt;
setupFrame();
t=newThread(this);
t.start();
pack();
show();
}
privatevoidsetupFrame(){
label=newJLabel();
label.setPreferredSize(newDimension(200,200));
Containerc=getContentPane();
JButtonstopButton=newJButton("Stop");
stopButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventae){
error();
}
});
c.add(label,BorderLayout.NORTH);
c.add(stopButton,BorderLayout.SOUTH);
}
privatevoidsetText(finalStrings){
try{
SwingUtilities.invokeAndWait(newRunnable(){
publicvoidrun(){
label.setText(s);
}
});
}catch(InterruptedExceptionie){
error();
}catch(InvocationTargetExceptionite){
error();
}
}
privatevoiderror(){
t.interrupt();
if(SwingUtilities.isEventDispatchThread())
closeDown();
elseSwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
closeDown();
}
});
}
privatevoidcloseDown(){
stt.setupCancelled();
hide();
dispose();
}
publicvoidrun(){
//Simulateconnectingtoserver
for(inti=0;i<stateMessages.length;i++){
setText(stateMessages[i]);
try{
Thread.sleep(5*1000);
}catch(InterruptedExceptionie){}
if(Thread.currentThread().isInterrupted())
return;
}
SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
stt.setupDone();
hide();
dispose();
}
});
}
}
We'veusedallourSwingutilitiesandtechniquesinthisexample.Thecomponentitselfisaframe,anditstartsanewthread.Everyfewseconds,thatthread
displaysanewstatusmessageintheframebycallingthesetText()method.Thatmethodisn'texecutingontheeventdispatchingthread,soitmustuse
theinvokeAndWait()methodtopassthetexttothelabel.Whenthethreadhasfinisheddisplayingstatusmessages(meaningthatintherealworld,ithas
connectedtotheserver),itinformstheSwingTypeTesterclassthatsetupiscompleteandsincethatclassexpectseverythingtorunontheevent
dispatchingthread,thesetupDone()methodmustbecalledfromaninvokeLater()method.
WhentheservergetsanerrorortheuserpressestheStopbutton,weneedtotelltheSwingTypeTestercomponentthatsetupwascancelled.Thecodeis
thesame,butthecontextisdifferent:theactionPerformed()methodrunsontheeventdispatchingthreadwhiletheexceptionintherun()methodruns
onaseparatethread.SowemustusetheisEventDispatchThread()methodtodeterminehowtocalltheSwingcomponents.
Summary
TheSwingclassescompriseoneofthelargestsetofclassesintheJavaAPI.WhilethreadsareanintegralpartofJava,theSwingclassesthemselvesarenot
threadsafe.Thisplacesaresponsibilityonthedeveloper,whomustmakesurethatshefollowstheappropriateaccesspatternsforSwingclasses.Methodson
Swingobjects(withafewexceptions)canbeinvokedonlyontheeventdispatchingthread.
Swing'suseoftheinvokeLater()methodgivesusahintabouthowwemighthandlethreadunsafelibrariesingeneral:aslongasaccesstothoselibraries
occursonlyonasinglethread,wewillnotrunintoanythreadingproblems.PassingaRunnableobjecttoathreadpoolthatcontainsasinglethreadis
preciselyanalogoustothetechniqueusedbytheSwingclasses.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
SwingTypeTester(allcomponentsthreadsafe)
javathreads.examples.ch07.example1.SwingTypeTester
ch7ex1
SwingTypeTester(usesinvokeAndWait)
javathreads.examples.ch07.example2.SwingTypeTester
ch7ex2
SwingTypeTesterwithsimulatedserverconnection
javathreads.examples.ch07.example3.SwingTypeTester
ch7ex3
Collection Interfaces
Aswementioned,thecollectionclassesarebasedaroundasetofinterfacesintroducedinJDK1.2:
java.util.List
Alistisanorderedsetofdata(e.g.,anarray).Unlikeactualarrays,listsarenotfixedinsizetheycangrowasmoredataisadded.Listsprovidemethods
togetandsetdataelementsbyindexandalsotoinsertorremovedataatarbitrarypoints(expandingorshrinkingthelistasnecessary).Therefore,they
canalsobethoughtofaslinkedlists.
java.util.Map
Amapassociatesvalueswithkeys.Duplicatekeysarenotallowedeachkeymapstoatmostonevalue.Thejava.util.SortedMapinterfaceextends
thistoprovidemapsthataresortedbasedonacollectionspecificdefinition.Thejava.util.Dictionaryinterfaceprovidesessentiallythesame
interfaceasamapbutis"obsolete"(unofficiallydeprecated).
java.util.Set
Asetisacollectionofelementsthatarestoredinnoparticularorder.Duplicateelementsarenotallowed.Thejava.util.SortedSetinterface
extendsthistoprovideasortedsetofobjects.
java.util.Queue
Aqueueisanorderedsetofdatathatisoperatedonineitherlastinfirstout(LIFO)orfirstinfirstout(FIFO)order(althoughnoimplementations
presentlysupportaLIFOordering).Previously,queuescouldbesimulatedbylists,butthenewqueueimplementationsaremoreefficient.Thisinterface
wasintroducedinJ2SE5.0.
java.util.Hashtable(aMap)
Asimple,unorderedmapofkeystovalues.
java.util.concurrent.ConcurrentHashMap(aMap)
Aclassthatimplementsanunorderedmap.ItuseslesssynchronizationthantheHashtableclass.
java.util.concurrent.CopyOnWriteArrayList(aList)
Asimplearraylistthatprovidessafesemanticsforunsynchronizediteratoraccess.
java.util.concurrent.CopyOnWriteArraySet(aSet)
Asimplesetthatprovidessafesemanticsforunsynchronizediteratoraccess.
java.util.concurrent.ConcurrentLinkedQueue(aQueue)
AnunboundedFIFOqueue.Itisoptimizedformultiplethreadsinsertingandremovingitemsfromthecollection.
java.util.HashMap(aMap)
Aclassthatimplementsanunorderedmapcollection.
java.util.WeakHashMap(aMap)
ThisclassissimilartotheHashMapclass.Thedifferenceisthatthekeyisaweakreferenceitisnotcountedasareferencebythegarbagecollector.
Theclassthereforedeleteskeyvaluepairentriesfromthemapwhenthekeyhasbeengarbagecollected.
java.util.TreeMap(aSortedMap)
Aclassthatimplementsasorted(andordered)mapcollection.Thismapisbasedonbinarytrees(sooperationsrequirelog(n)timetoperform).
java.util.ArrayList(aList)
Aclassthatimplementsalistcollection.Internally,itisimplementedusingarrays.
java.util.LinkedList(aListandaQueue)
Aclassthatimplementsalistandaqueuecollection,providingadoublylinkedlist.
java.util.LinkedHashSet(aSet)
Asetcollectionthatsortsitsitemsbasedontheorderinwhichtheyareaddedtotheset.
java.util.LinkedHashMap(aMap)
Amapcollectionthatsortsitsitemsbasedontheorderinwhichtheyareaddedtothemap.
java.util.IdentityHashMap(aMap)
Amapcollection.Unlikeallothermaps,thisclassuses==forkeycomparisoninsteadoftheequals()method.
java.util.EnumSet(aSet)
AspecializedsetcollectionthatholdsonlyEnumvalues.
java.util.EnumMap(aMap)
AspecializedmapcollectionthatusesonlyEnumvaluesaskeys.
java.util.PriorityQueue(aQueue)
Anunboundedqueueinwhichretrievalisnotbasedonorder(LIFOorFIFO)instead,objectsareremovedaccordingtowhichisthesmallest(as
determinedbytheComparableorComparatorinterface).
java.util.concurrent.ArrayBlockingQueue(aQueue)
AboundedFIFOqueue.Thiscollectionsupportstheblockinginterface,aninterfacethatallowsthreadstowaiteitherforspacetobeavailable(while
storingdata)ordatatobeavailable(whileretrievingdata).
java.util.concurrent.LinkedBlockingQueue(aQueue)
AFIFOqueuethatcanbeeitherboundedorunbounded.Thiscollectionsupportstheblockinginterface.
java.util.concurrent.SynchronousQueue(aQueue)
AboundedFIFOqueue.Theboundonthisqueueisone(noelementsareactuallyheldinthecollection),andmultiplethreadsoperateonit
synchronously.
java.util.concurrent.PriorityBlockingQueue(aQueue)
AthreadsafeimplementationofthePriorityQueueclass.Thisclassalsosupportstheblockinginterface.
java.util.concurrent.DelayQueue(aQueue)
Aclassthatimplementsanunboundedqueuewithatimebasedorder.RetrievalfromthequeueisbasedontheobjectwhosegetDelay()methodhas
expiredearliest:elementswhosetimeexpirationhasnotoccurredcan'tberetrievedfromthequeue.
Simple Synchronization
Let'stakethesimplecasefirst.Inthesimplecase,you'regoingtousethecollectionclasstostoreshareddata.Otherthreadsretrievedatafromthecollection,
buttherewon'tbemuch(ifany)manipulationofthedata.
Inthiscase,theeasiestobjecttouseisathreadsafecollection(e.g.,aVectororHashtable).That'swhatwe'vedoneallalonginour
CharacterEventHandlerclass:
packagejavathreads.examples.ch08.example1;
importjava.util.*;
publicclassCharacterEventHandler{
privateVectorlisteners=newVector();
publicvoidaddCharacterListener(CharacterListenercl){
listeners.add(cl);
}
publicvoidremoveCharacterListener(CharacterListenercl){
listeners.remove(cl);
}
publicvoidfireNewCharacter(CharacterSourcesource,intc){
CharacterEventce=newCharacterEvent(source,c);
CharacterListener[]cl=(CharacterListener[])
listeners.toArray(newCharacterListener[0]);
for(inti=0;i<cl.length;i++)
cl[i].newCharacter(ce);
}
}
Inthiscase,usingavectorissufficientforourpurposes.Ifmultiplethreadscallmethodsofthisclassatthesametime,thereisnoconflict.Becausethe
listenerscollectionisthreadsafe,wecancallitsadd(),remove(),andtoArray()methodsatthesametimewithoutcorruptingtheinternalstateof
theVectorobject.Strictlyspeaking,thereisaraceconditionhereinouruseofthetoArray()methodwe'lltalkaboutthatalittlemoreinthenextsection.
ButthepointisthatnoneofthemethodsonthevectorseedatainaninconsistentstatebecausetheVectorclassitselfisthreadsafe.
Asecondoptionwouldbetouseathreadunsafeclass(e.g.,theArrayListclass)andmanagethesynchronizationexplicitly:
packagejavathreads.examples.ch08.example2;
...
publicclassCharacterEventHandler{
privateArrayListlisteners=newArrayList();
publicsynchronizedvoidaddCharacterListener(CharacterListenercl){
...
}
publicsynchronizedvoidremoveCharacterListener(CharacterListenercl){
...
}
publicsynchronizedvoidfireNewCharacter(CharacterSourcesource,intc){
...
}
}
Orwecouldhavesynchronizedtheclasslikethis:
packagejavathreads.examples.ch08.example3;
...
publicclassCharacterEventHandler{
privateArrayListlisteners=newArrayList();
publicvoidaddCharacterListener(CharacterListenercl){
synchronized(listeners){
listeners.add(cl);
}
}
publicvoidremoveCharacterListener(CharacterListenercl){
synchronized(listeners){
listeners.add(cl);
}
}
publicvoidfireNewCharacter(CharacterSourcesource,intc){
CharacterEventce=newCharacterEvent(source,c);
CharacterListener[]cl;
synchronized(listeners){
cl=(CharacterListener[])
listeners.toArray(newCharacterListener[0]);
}
for(inti=0;i<cl.length;i++)
cl[i].newCharacter(ce);
}
}
Inthisexample,itdoesn'tmatterwhetherwesynchronizeonthecollectionobjectortheeventhandlerobject(this)eitheroneensuresthattwothreadsare
notsimultaneouslycallingmethodsoftheArrayListclass.
Ourthirdoptionistouseasynchronizedversionofthethreadunsafecollectionclass.Mostthreadunsafecollectionclasseshaveasynchronizedcounterpart
thatisthreadsafe.ThethreadsafecollectionsareconstructedbycallingoneofthesestaticmethodsoftheCollectionsclass:
Sets=Collections.synchronizedSet(newHashSet(...));
Sets=Collections.synchronizedSet(newLinkedHashSet(...));
SortedSets=Collections.synchronizedSortedSet(newTreeSet(...));
Sets=Collections.synchronizedSet(EnumSet.noneOf(obj.class));
Mapm=Collections.synchronizedMap(newHashMap(...));
Mapm=Collections.synchronizedMap(newLinkedHashMap(...));
SortedMapm=Collections.synchronizedSortedMap(newTreeMap(...));
Mapm=Collections.synchronizedMap(newWeakHashMap(...));
Mapm=Collections.synchronizedMap(newIdentityHashMap(...));
Mapm=Collections.synchronizedMap(newEnumMap(...));
Listlist=Collections.synchronizedList(newArrayList(...));
Listlist=Collections.synchronizedList(newLinkedList(...));
Anyoftheseoptionsprotectaccesstothedataheldinthecollection.Thisisaccomplishedbywrappingthecollectioninanobjectthatsynchronizesevery
methodofthecollectioninterface:itisnotdesignedasanoptimallysynchronizedclass.Alsonotethatthequeuecollectionisnotsupported:the
CollectionsclasssuppliesonlywrapperclassesthatsupporttheSet,Map,andListinterfaces.Thisisnotaprobleminmostcasessincethemajorityof
thequeueimplementationsaresynchronized(andsynchronizedoptimally).
Complex Synchronization
Amorecomplexcaseariseswhenyouneedtoperformmultipleoperationsatomicallyonthedataheldinthecollection.Intheprevioussection,wewereable
tousesimplesynchronizationbecausethemethodsthatneededtoaccessthedatainthecollectionperformedonlyasingleoperation.The
addCharacterListener()methodhasonlyasinglestatementthatusesthelistenersvector,soitdoesn'tmatterifthedatachangesafterthe
addCharacterListener()methodcallsthelisteners.add()method.Asaresult,wecouldrelyonthecontainertoprovidethesynchronization.
WealludedtoaraceconditioninthefireNewCharacter()method.Afterwecallthelisteners.toArray()method,wecyclethroughthelisteners
tocalleachofthem.It'sentirelypossiblethatanotherthreadwillcalltheremoveCharacterListener()methodwhilewe'reloopingthroughthearray.
Thatwon'tcorruptthearrayorthelistenersvector,butinsomealgorithms,itcouldbeaproblem:we'dbeoperatingondatathathasbeenremovedfrom
thevector.Inourprogram,that'sokay:wehaveabenignracecondition.Inotherprograms,thatmaynotnecessarilybethecase.
Supposewewanttokeeptrackofallthecharactersthatplayerstypedcorrectly(orincorrectly).Wecoulddothatwiththefollowing:
packagejavathreads.examples.ch08.example4;
importjava.util.*;
importjavax.swing.*;
importjavax.swing.table.*;
publicclassCharCounter{
publicHashMapcorrectChars=newHashMap();
publicHashMapincorrectChars=newHashMap();
privateAbstractTableModelatm;
publicvoidcorrectChar(intc){
synchronized(correctChars){
Integerkey=newInteger(c);
Integernum=(Integer)correctChars.get(key);
if(num==null)
correctChars.put(key,newInteger(1));
elsecorrectChars.put(key,newInteger(num.intValue()+1));
if(atm!=null)
atm.fireTableDataChanged();
}
}
publicintgetCorrectNum(intc){
synchronized(correctChars){
Integerkey=newInteger(c);
Integernum=(Integer)correctChars.get(key);
if(num==null)
return0;
returnnum.intValue();
}
}
publicvoidincorrectChar(intc){
synchronized(incorrectChars){
Integerkey=newInteger(c);
Integernum=(Integer)incorrectChars.get(key);
if(num==null)
incorrectChars.put(key,newInteger(1));
elseincorrectChars.put(key,newInteger(num.intValue()1));
if(atm!=null)
atm.fireTableDataChanged();
}
}
publicintgetIncorrectNum(intc){
synchronized(incorrectChars){
Integerkey=newInteger(c);
Integernum=(Integer)incorrectChars.get(key);
if(num==null)
return0;
returnnum.intValue();
}
}
publicvoidaddModel(AbstractTableModelatm){
this.atm=atm;
}
}
Hereweusethreadunsafecollectionstoholdthedataandexplicitlysynchronizeaccessaroundthecodethatusesthecollections.Itwouldbeinsufficientto
useHashtablecollectionsinthiscodewithoutalsosynchronizingaswedidearlier.Althoughretrievingavaluefromahashtableisthreadsafe,and
replacinganelementinahashtableisalsothreadsafe,theoveralloperationisnotthreadsafe:bothcollectionoperationsmustbeatomicforthealgorithmto
succeed.Otherwise,twothreadscouldsimultaneouslyretrievethestoredvalue,incrementit,andstoreitthenetresultwouldbeascorethatisonelessthanit
shouldbe.
Themoralofthestoryisthatusingathreadsafecollectiondoesnotguaranteethecorrectnessofyourprogram.Becauseoftheexplicitsynchronization
requiredinthisexample,wewereabletouseathreadunsafecollection(although,aswe'llseeinChapter14,ifyouuseathreadsafecollection,it'sunlikely
you'llseemuchdifference.)
packagejavathreads.examples.ch08.example5;
...
publicvoidfireNewCharacter(CharacterSourcesource,intc){
CharacterEventce=newCharacterEvent(source,c);
Enumeratione;
synchronized(listeners){
e=listeners.elements();
while(e.hasMoreElements()){
((CharacterListener)e.nextElement()).newCharacter(ce);
}
}
}
}
Youcouldsynchronizethemethodinstead,aslongasyourcollectionisnotusedinanyunsynchronizedmethod.Thepointisthattheenumerationandall
usesofthecollectionmustbelockedbythesamesynchronizationobject.
Iteratorsbehavesomewhatdifferently.Iftheunderlyingcollectionofaniteratorismodifiedwhiletheiteratorisactive,thenextaccesstotheiteratorthrowsa
ConcurrentModificationException,whichisalsoaruntimeexception.Unlikeenumerations,iftheiteratorfails,theunderlyingcollectioncanstillbe
used.Thewayinwhichiteratorsfailimmediatelyafteramodifyoperationiscalled"failfast."
Thesafestwaytouseaniteratoristomakesureitsuseissynchronizedbyitsunderlyingcollection(justaswedidwiththeenumeration)ortomakesure
thatitandthecollectionareprotectedbythesamesynchronizationlock.
Youcan'trelyuponthefailfastnatureofiterators.Iteratorsmakeabesteffortatdeterminingwhentheunderlyingcollectionhaschanged,butintheabsence
ofsynchronization,it'simpossibletopredictwhenthefailureoccurs.Onceafailurehasoccurred,theiteratorisnotusefulforfurtherprocessing.Therefore,
you'releftwithasituationwheresomeelementsofthecollectionhavebeenprocessedandothershavenot.
TwoclassesCopyOnWriteArrayListandCopyOnWriteArraySetprovidespecialiterationsemantics.Theseclassesaredesignedtocopythe
underlyingcollectionwhennecessarysothatiteratorsoperateonasnapshotofthedatafromthetimetheiteratorwascreated.Modifyingthecollectionwhile
theiteratorisactivecreatesacopyofthecollectionfortheiterator.
Thisisanexpensiveoperation,bothintermsoftimeandmemoryusage.However,itensuresthatiteratorscanbeusedfromunsynchronizedcodebecause
theiteratorsendupoperatingonoldcopiesofthedata.So,theiteratorsneverthrowaconcurrentmodificationexception.
Theseclassesaredesignedforcaseswheremodificationstothecollectionarerareandtheiteratorofthecollectionisusedfrequentlybymultiplethreads.This
allowstheiteratorstobeunsynchronizedandstillbethreadsafeaslongastheupdatesarerareenough,thisyieldsbetteroverallperformance.Note,however,
thatraceconditionsarestillpossiblewiththistechniqueit'sessentiallythesametypeofoperationaswesawearlierwiththetoArray()method.The
differenceiswhenthecopyingoccurs:whenyoucallthetoArray()method,acopyofthecollectionismadeatthattime.Withthecopyonwriteclasses,
thecopyismadewheneverthecollectionismodified.
Thread-Aware Classes
Manycollectionclassesarewhatwewouldterm"threadaware."Theyhavemanyinternalandsubtlefeaturesthatweredesignedspecificallyforthreads:
Somecollectionshaveanimplementationthatminimizestheneedforsynchronizationbysegmentingthecollection.Itispossibleforthreadstomodify
thecollectionsimultaneously,withoutanysynchronization,whentheyareoperatingondifferentsegments.
Someprovidespecialservicessuchasiteratorhandlingthatarespecificallydesignedformultithreadedenvironments.Themainreasonforcopyon
writeiteratorsistobalancetheperformanceissuesofmanysimultaneousthreadsiteratingthroughthecollectionagainstafewupdatestothecollection.
Interfaceshavebeenenhancedtohandleissuesrelatedtothreadsbetter.Forexample,theconcurrenthashmaphastheabilitytoaddakeyonlyifthekeyis
notinthemapthissimpleenhancementremovestheneedforexplicitsynchronizationforparallelwritesofnewelements.
Figure81.Theproducer/consumerpattern
Theproducer/consumerpatterniscommonforthreadedprogramsbecauseitiseasytomakethreadsafe.Wejustneedtoprovideasafewaytopassdatafrom
theproducertotheconsumer.Dataneedstobesynchronizedonlyduringthesmallperiodoftimewhenitisbeingpassedbetweenproducerandconsumer.
Wecanusesimplesynchronizationsincetheactsofinsertingandremovingfromthecollectionaresingleoperations.Therefore,anythreadsafevector,list,or
queuecanbeused.
ThequeuebasedcollectionclassesaddedtoJ2SE5.0werespecificallydesignedforthismodel.Thequeuedatatypeisperfecttouseforthispatternsinceit
hasthesimplesemanticsofaddingandremovingasingleelement(withanoptionalorderingoftherequests).Furthermore,blockingqueuesprovidethread
controlfunctionality:thisallowsyoutofocusonthefunctionalityofyourprogramwhilethequeuetakescareofthreadandspacemanagementissues.Of
course,ifyouneedcontroloversuchissues,youcanuseanonblockingqueueanduseyourownexplicitsynchronizationandnotification.
Here'sasimpleproducerthatusesablockingqueue:
packagejavathreads.examples.ch08.example6;
importjava.util.*;
importjava.util.concurrent.*;
publicclassFibonacciProducerimplementsRunnable{
privateThreadthr;
privateBlockingQueue<Integer>queue;
publicFibonacciProducer(BlockingQueue<Integer>q){
queue=q;
thr=newThread(this);
thr.start();
}
publicvoidrun(){
try{
for(intx=0;;x++){
Thread.sleep(1000);
queue.put(newInteger(x));
System.out.println("Producedrequest"+x);
}
}catch(InterruptedExceptionex){
}
}
}
Theproducerisimplementedtoruninaseparatethreaditusesthequeuetostorerequeststobeprocessed.We'reusingablockingqueuebecausewewant
thequeuetohandlethecasewheretheproducergetstoofaraheadoftheconsumer.Whenthathappens,wewanttheproducertoblock(sothatitdoesnot
produceanymorerequestsuntiltheconsumercatchesup).
Here'stheconsumer:
packagejavathreads.examples.ch08.example6;
importjava.util.concurrent.*;
publicclassFibonacciConsumerimplementsRunnable{
privateFibonaccifib=newFibonacci();
privateThreadthr;
privateBlockingQueue<Integer>queue;
publicFibonacciConsumer(BlockingQueue<Integer>q){
queue=q;
thr=newThread(this);
thr.start();
}
publicvoidrun(){
intrequest,result;
try{
while(true){
request=queue.take().intValue();
result=fib.calculateWithCache(request);
System.out.println(
"Calculatedresultof"+result+"from"+request);
}
}catch(InterruptedExceptionex){
}
}
}
Theconsumeralsorunsinitsownthread.Itblocksuntilarequestisinthequeue,atwhichpointitcalculatesaFibonaccinumberbasedontherequest.The
actualcalculationisperformedbytheFibonacciclassavailableintheonlineexamples(alongwithatestingprogram).
Noticethattheproducerandconsumerthreadsaredecoupled:theproducerneverdirectlycallstheconsumer(andviceversa).Thisallowsustointerchange
differentproducerswithoutaffectingtheconsumer.Italsoallowsustohavemultipleproducersservicedbyasingleconsumer,ormultipleconsumers
servicingasingleproducer.Moregenerally,wecanvarythenumberofeitherbasedonperformanceneedsoruserrequirements.
Thequeuehasalsohiddenalloftheinterestingthreadcode.Whenthequeueisfull,theproducerblocks:itwaitsonaconditionvariable.Later,whenthe
consumertakesanelementfromthequeue,itnotifiesthewaitingproducer.Asimilarsituationariseswhentheconsumercallsthetake()methodonan
emptyqueue.Youcouldwritealltheconditionvariablecodetohandlethis,butit'sfareasiertoallowthequeuetodoitforyou.
WechosetocalculateaFibonaccinumberinourtestprogrambecauseweusedarecursivealgorithmthattakesanincreasinglylongtimetocompute.It's
interestingtowatchhowtheproducerandconsumerinteractinthiscase.Inthebeginning,theconsumerisblockedalotofthetimebecauseitcancalculate
theFibonaccinumberinlessthanonesecond(thetimeperiodbetweenrequestsfromtheproducer).Later,theproducerspendsmostofitstimeblocked
becauseithasoverwhelmedtheconsumerandfilledthequeue.
Ifyouhaveamultiprocessormachine,youcanruntheexamplewithmultipleconsumerthreads,buteventuallytheresultisthesame:thecalculationstaketoo
longfortheconsumerstokeepup.
Summary
Inthischapter,wehaveexaminedhowthreadsinteractwithJava'scollectionclasses.We'veseenthesynchronizationrequirementsimposedbydifferent
classesandhowtohandlethoserequirementseffectively.We'vealsoexaminedhowtheseclassescanbeusedforthecommondesignpatternknownasthe
producer/consumerpattern.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter.Theonlineexamplesalsoincludetestcodefortheproducer/consumerpattern.
Description
MainJavaclass
Anttarget
SwingTypeTester
javathreads.examples.ch08.example1.SwingTypeTester
ch8ex1
SwingTypeTester(usesarraylists)
javathreads.examples.ch08.example2.SwingTypeTester
ch8ex2
SwingTypeTester(usessynchronizedblocks)
javathreads.examples.ch08.example3.SwingTypeTester
ch8ex3
SwingTypeTester(countscharactersuccess/failures)
javathreads.examples.ch08.example4.SwingTypeTester
ch8ex4
SwingTypeTester(usesenumeration)
javathreads.examples.ch08.example5.SwingTypeTester
ch8ex5
Producer/ConsumerModel
javathreads.examples.ch08.example6.FibonacciTestnConsumers
ch8ex6
IntheAntscript,thenumberofconsumerthreadsisdefinedbythisproperty:
<propertyname="nConsumers"value="1"/>
Chapter9.Thread Scheduling
Theterm"threadscheduling"coversavarietyoftopics.Thischapterexaminesoneofthosetopics,whichishowacomputerselectsparticularthreadstorun.
Theinformationinthischapterprovidesabasicunderstandingofwhenthreadsrunandhowcomputershandlemultiplethreads.There'slittleprogramming
inthischapter,buttheinformationwepresentisanimportantfoundationforothertopicsofthreadscheduling.Inparticular,thenextfewchaptersdiscusstask
schedulingandthreadpools,whicharetheprogrammatictechniquesyouusetomanagelargenumbersofthreadsandjobs.
ThekeytounderstandingJavathreadschedulingistorealizethataCPUisascarceresource.Whentwoormorethreadswanttorunonasingleprocessor
machine,theyendupcompetingfortheCPU,andit'suptosomeoneeithertheprogrammer,theJavavirtualmachine,ortheoperatingsystemtomake
surethattheCPUissharedamongthesethreads.ThesameistruewheneveraprogramhasmorethreadsthanthemachinehostingtheprogramhasCPUs.
TheessenceofthischapteristounderstandhowCPUsaresharedamongthreadsthatwanttoaccessthem.
Inearlierexamples,wedidn'tconcernourselveswiththistopicbecause,inthosecases,thedetailsofthreadschedulingweren'timportanttous.Thiswas
becausethethreadswewereconcernedwithdidn'tnormallycompeteforaCPU:theyhadspecifictaskstodo,butthethreadsthemselveswereusuallyshort
livedoronlyperiodicallyneededaCPUinordertoaccomplishtheirtask.Considertheeventprocessingthreadinourtypingprogram.Mostofthetime,this
threadisn'tusingaCPUbecauseit'swaitingfortheusertodosomething.Whentheusertypesacharacterormovesthemouse,thethreadquicklyprocesses
theeventandwaitsforthenexteventsincethethreaddoesn'tneedaCPUveryoften,wedidn'tneedtoconcernourselveswiththethread'sscheduling.
ThetopicofthreadschedulingisadifficultonetoaddressbecausetheJavaspecificationdoesnotrequireimplementationstoschedulethreadsinaparticular
manner.Itprovidesguidelinesthatthreadsshouldbescheduledbasedonathread'spriority,buttheyarenotabsolute,anddifferentimplementationsofthe
Javavirtualmachinefollowtheguidelinesdifferently.YoucannotguaranteetheorderofexecutionofthreadsacrossallJavavirtualmachines.
packagejavathreads.examples.ch09;
importjava.util.*;
importjava.text.*;
publicclassTaskimplementsRunnable{
longn;
Stringid;
privatelongfib(longn){
if(n==0)
return0L;
if(n==1)
return1L;
returnfib(n1)+fib(n2);
}
publicTask(longn,Stringid){
this.n=n;
this.id=id;
}
publicvoidrun(){
Dated=newDate();
DateFormatdf=newSimpleDateFormat("HH:mm:ss:SSS");
longstartTime=System.currentTimeMillis();
d.setTime(startTime);
System.out.println("Startingtask"+id+"at"+df.format(d));
fib(n);
longendTime=System.currentTimeMillis();
d.setTime(endTime);
System.out.println("Endingtask"+id+"at"+df.format(d)+
"after"+(endTimestartTime)+"milliseconds");
}
}
We'vemadethisclassaRunnableobjectsothatwecanrunmultipleinstancesofitinmultiplethreads:
packagejavathreads.examples.ch09.example1;
importjavathreads.examples.ch09.*;
publicclassThreadTest{
publicstaticvoidmain(String[]args){
intnThreads=Integer.parseInt(args[0]);
longn=Long.parseLong(args[1]);
Threadt[]=newThread[nThreads];
for(inti=0;i<t.length;i++){
t[i]=newThread(newTask(n,"Task"+i));
t[i].start();
}
for(inti=0;i<t.length;i++){
try{
t[i].join();
}catch(InterruptedExceptionie){}
}
}
}
Runningthiscodewiththreethreadsproducesthiskindofoutput:
StartingtaskTask2at00:04:30:324
StartingtaskTask0at00:04:30:334
StartingtaskTask1at00:04:30:345
EndingtaskTask1at00:04:38:052after7707milliseconds
EndingtaskTask2at00:04:38:380after8056milliseconds
EndingtaskTask0at00:04:38:502after8168milliseconds
Let'slookatthisoutput.Noticethatthelastthreadwecreatedandstarted(Task2)wasthefirstonethatprinteditsfirstoutput.However,allthreadsstarted
within20millisecondsofeachother.Theactualcalculationtookabouteightsecondsforeachthread,andthethreadsendedinadifferentorderthanthey
startedin.Inparticular,eventhoughTask2startedfirst,ittook349millisecondslongertoperformthesamecalculationasTask1andfinishedafterTask1.
Generally,we'dexpecttoseesimilaroutputonalmostanyJavavirtualmachinerunningonalmostanyplatform:thethreadswouldstartatalmostthesame
timeinsomerandomorder,andtheywouldendina(different)randomorderafterhavingrunforaboutthesameamountoftime.
Certainvirtualmachinesandoperatingsystems,however,wouldproducethisoutput:
StartingtaskTask0at00:04:30:324
EndingtaskTask0at00:04:33:052after2728milliseconds
StartingtaskTask1at00:04:33:062
EndingtaskTask1at00:04:35:919after2857milliseconds
StartingtaskTask2at00:04:35:929
EndingtaskTask2at00:04:37:720after2791milliseconds
Thetotalheretakesaboutthesameamountoftime,butnowtheyhaverunsequentially:thesecondtaskdidnotbegintoexecuteuntilthefirsttaskwas
finished.Anotherinterestingfactaboutthisoutputisthateachindividualtasktooklesstimethanitdidpreviously.That'satopicwe'llcoverinChapter10.
Priority-Based Scheduling
Ineachoftheseexamples,multiplethreadscompetefortimeontheCPU.Whenmultiplethreadswanttoexecute,itisuptotheunderlyingoperatingsystem
todeterminewhichofthosethreadsareplacedonaCPU.Javaprogramscaninfluencethatdecisioninsomeways,butthedecisionisultimatelyuptothe
operatingsystem.
AJavavirtualmachineisrequiredtoimplementapreemptive,prioritybasedscheduleramongitsvariousthreads.ThismeansthateachthreadinaJava
programisassignedacertainpriority,apositiveintegerthatfallswithinawelldefinedrange.Thisprioritycanbechangedbythedeveloper.TheJavavirtual
machineneverchangesthepriorityofathread,evenifthethreadhasbeenrunningforacertainperiodoftime.
ThepriorityvalueisimportantbecausethecontractbetweentheJavavirtualmachineandtheunderlyingoperatingsystemisthattheoperatingsystemmust
generallychoosetoruntheJavathreadwiththehighestpriority.That'swhatwemeanwhenwesaythatJavaimplementsaprioritybasedscheduler.This
schedulerisimplementedinapreemptivefashion,meaningthatwhenahigherprioritythreadcomesalong,thatthreadinterrupts(preempts)whateverlower
prioritythreadisrunningatthetime.Thecontractwiththeoperatingsystem,however,isnotabsolute,whichmeansthattheoperatingsystemcansometimes
choosetorunalowerprioritythread.
Java'srequirementforaprioritybased,preemptiveschedulingmechanismmapswelltomanyoperatingsystems.Solaris,thevariousWindowsoperating
systems,Linux,andmostotheroperatingsystemsondesktopcomputersandserversallprovidethesupportforthatkindofthreadscheduling.Certain
operatingsystems,particularlythoseonspecializeddevicesandonsmaller,handhelddevices,donotprovidethatlevelofschedulingsupportJavavirtual
machineimplementationsforthoseoperatingsystemsmustperformthenecessarythreadschedulingontheirown.
Ourfirstexample,wherethethreadsallcompleteataboutthesametime,isexecutedonastandardoperatingsystem(Solaris)wherethethreadschedulingis
handledbytheoperatingsystem.Oursecondexample,wherethethreadsrunsequentially,isfromasystemwheretheJavavirtualmachineitselfhandlesthe
threadscheduling.BothimplementationsarevalidJavavirtualmachines.
Initial
Athreadobjectisintheinitialstatefromtheperiodwhenitiscreated(thatis,whenitsconstructoriscalled)untilthestart()methodofthethread
objectiscalled.
Runnable
Athreadisintherunnablestateonceitsstart()methodhasbeencalled.Athreadleavestherunnablestateinvariousways,buttherunnablestatecan
bethoughtofasadefaultstate:ifathreadisn'tinanyotherstate,it'sintherunnablestate.
AthreadthatisintherunnablestatemaynotactuallyberunningitmaybewaitingforaCPU.AthreadthatisrunningonaCPUiscalledacurrently
runningthread.
Blocked
Athreadthatisblockedisonethatcannotberunbecauseitiswaitingforsomespecificeventtooccur.Threadsblockformanyreasons:theyattemptto
readdata(e.g.,fromasocket)whennodataisavailabletheyexecuteathreadblockingmethod(e.g.,thesleep(),wait(),orjoin()methods)or
theyattempttoacquireasynchronizationlockthatanotherthreadalreadyholds.We'veseenAPIsthatalsoblock,butinternallythosemethodsareall
executingthewait()method.
Exiting
Athreadisintheexitingstateonceitsrun()methodreturns(oritsdeprecatedstop()methodhasbeencalled).
Thebasicprocessofthreadschedulingisessentiallythesamewhetherit'sperformedbytheJavavirtualmachineortheunderlyingoperatingsystem.Our
intenthereistoprovideanillustrationofhowthreadschedulingworks,nottoprovideablueprintofhowanyparticularthreadschedulerisactually
implemented.
Wecanconceivethatathreadschedulerkeepstrackofallthethreadsonwhichitoperatesbyusinglinkedlistseverythreadisonalistthatrepresentsthestate
ofthethread.AJavathreadcanhaveoneof11priorities,soweconceiveof14linkedlists:oneforallthreadsintheinitialstate,oneforallthreadsinthe
blockedstate,oneforallthreadsintheexitingstate,andoneforeachprioritylevel.Thelistofthreadsatagivenprioritylevelrepresentsonlythosethreads
thatarecurrentlyintherunnablestate:athreadintherunnablestateatpriority7isplacedonthepriority7list,butwhenthethreadblocks,itmovestothe
blockedlinkedlist.We'respeakinghereofhaving11priorities,butthatnumberisaJavaabstraction:anoperatingsystemmayhavemoreorfewerpriorities
thanthat(butconceptually,eachwouldstillhaveitsownlinkedlist).
Forsimplicity,weconceiveofthesethreadsasbeingonanorderedlistinreality,theymaybeheldinsimplepools.Keepingthethreadsinalinkedlist
impliesthatthreadswillbeselectedtoruninaparticularorder.Whilethatisausefulwayofthinkingabouttheprocess,itisnotnecessarilythewayan
implementationmaywork.
Let'sseehowthisschedulingwilloccurwiththeexampleweshowatthebeginningofthechapter.Thatexamplehasatotaloffourthreads:theinitialthread
(whichexecutesthemain()method)andthethreetaskthreadswestarted.Infact,aswe'vementioned,therearemorethreadsbecausethevirtualmachine
startsvariousbackgroundthreads(likethegarbagecollectionthread).Butforourdiscussion,we'llconsideronlythefourthreadsthatareexecutingourcode.
ThethreadsthatcalculateaFibonaccinumberneverblock:theymovefromtheinitialstatetotherunnablestatetotheexitingstate.Themainthreadisinthe
runnablestateandthenenterstheblockingstatewhenitexecutesthejoin()methodtowaitfortheotherthreads.
Thesecondtimethatweruntheprogram,thestateofthethreadsfollowsthetransitionpathshowninFigure91.Themainthreadisthecurrentlyrunning
threaduntilitblocksattimeT1.Atthatpoint,oneofthetaskthreadsbecomesthecurrentlyrunningthreaditremainsthecurrentlyrunningthreaduntiltime
T2whenitfinishesandtransitionstotheexitingstate.Anothertaskthreadbecomesthecurrentlyrunningthread,andthecyclecontinuesuntilallthreadshave
completed.
Figure91.Asimplethreadstatediagram
Thatexplainstheoutputthatweseewhenweruntheprogramforasecondtime:everything(includingtheoutput)proceedssequentially.Sowhyisthe
outputdifferentthefirsttimeweruntheexample?
Thefirsttimeweruntheexample,wedosoonatypicaloperatingsystem.ThethreadscheduleronthatOS,inadditiontobeingprioritybasedand
preemptive,isalsotimeslicing.ThatmeanswhenthreadsarewaitingfortheCPU,theoperatingsystemallowsoneofthemtorunforaveryshorttime.It
theninterruptsthatthreadandallowsasecondthreadtorunforaveryshorttime,andsoon.Aportionofthethreadtransitionsonsuchanoperatingsystem
isshowninFigure92.
Figure92.ThreadstateswithOSscheduling
Javadoesnotmandatethatitsthreadsbetimesliced,butmostoperatingsystemsdoso.Thereisoftensomeconfusioninterminologyhere:preemptionis
oftenconfusedwithtimeslicing.Infact,preemptionmeansonlythatahigherprioritythreadrunsinsteadofalowerpriorityone,butwhenthreadshavethe
samepriority,theydonotpreempteachother.Theyaretypicallysubjecttotimeslicing,butthatisnotarequirementofJava.
There'soneotherimportantpointaboutthesetwofigures.Inourfirstfigure,thetimepoints(T1,T2,andsoon)arerelativelyfarapart.Thetimetransitions
inthatcasearedeterminedwhenaparticularthreadchangesstate:whenthemainthreadchangestotheblockedstate,ataskthreadchangestobecomethe
currentlyrunningthread.Whenthatthreadchangestotheexitingstate,asecondtaskthreadchangestobecomethecurrentlyrunningthreadandsoon.
Inthesecondcase,thetimetransitionsoccuratamuchshorterinterval,ontheorderofafewhundredmillisecondsorso.Inthiscase,thetransitionsofthe
threadsbetweencurrentlyrunningandwaitingforCPUareimposedbytheoperatingsystemandnotasaresultofanythingthethreaditselfhasdone.Of
course,ifathreadvoluntarilychangestotheexitingorwaitingstate,atransitionoccursatthatpointaswell.
Priority Exceptions
WhenanoperatingsystemschedulesJavathreads,itmaychoosetorunalowerprioritythreadinsteadofahigherprioritythreadintwoinstances,described
next.
PRIORITY INVERSION
Inatypicalprioritybasedthreadingsystem,somethingunusualoccurswhenathreadattemptstoacquirealockthatisheldbyalowerprioritythread:
becausethehigherprioritythreadbecomesblocked,ittemporarilyrunswithaneffectivepriorityofthelowerprioritythread.Supposethatwehaveathread
withapriorityof8thatwantstoacquirealockthatisheldbyathreadwithapriorityof2.Becausethepriority8threadiswaitingforthepriority2threadto
releasethelock,itendsuprunningwithaneffectivepriorityof2.Thisisknownaspriorityinversion.
Priorityinversionisoftensolvedbypriorityinheritance.Withpriorityinheritance,athreadthatholdsalockthatiswantedbyathreadwithahigherpriority
hasitsprioritytemporarilyandsilentlyraised:itsnewprioritybecomesthesameasthepriorityofthethreadthatitiscausingtoblock.Whenthethread
releasesthelock,itspriorityisloweredtoitsoriginalvalue.
Thegoalofpriorityinheritanceistoallowthehighprioritythreadtorunassoonaspossible.Itisacommonfeatureofoperatingsystems,andJavavirtual
machinesrunningonthoseoperatingsystemsaresubjecttoit.However,itisnotarequirementoftheJavaspecification.
COMPLEX PRIORITIES
Thesecondcaseinvolvesthepriorityassignedtothreadsbytheoperatingsystem.WementionedthatJavahas11prioritylevels(10ofwhichareavailableto
developers),butthisisanabstractionoftheJavalanguage.Operatingsystemsusuallyhavemanymorepriorities.Moreimportant,though,isthatthepriority
thattheoperatingsystemassignstoathreadisacomplexformulathattakesmanypiecesofinformationintoaccount.
Asimpleversionofthisformulamightbethis:
RealPriority=JavaPriority+SecondsWaitingForCPU
ThistypeofformulaaccountsforthelengthoftimethatthethreadhasbeenwaitingforaCPU.Afterasufficientamountoftimehaspassed,athreadwitha
Javapriorityof3hasarealprioritythatishigherthanacurrentlyrunningJavathreadwithapriorityof5.Thisgivesthepriority3threadanopportunityto
run,eventhoughithasalowerprioritythanotherunblockedthreads.
Complexprioritiesareadvantageousbecausetheyhelptopreventthreadstarvation.Withoutsuchamodel,alowprioritythreadwouldhavetowaitforall
otherhighprioritythreadstoblockbeforeitisgivenachancetoexecuteit'slikelythatitmighthavetowaitforever.Withcomplexpriorities,itcanstillrun
muchlessoftenthanitshigherprioritypeers,butatleastitwillrunsometimes.
Ontheotherhand,complexprioritiesmeanthatyoucannotguaranteethreadscheduling.Inparticular,youcannotusethreadprioritiestotryandpreventrace
conditionsindataaccess:alowerprioritythreadcaninterruptahigherprioritythreadwhileitisintheprocessofupdatingshareddata.Youalsocannotuse
threadprioritiestoensureacertainorderofexecutionbetweentasks.
packagejava.lang;
publicclassThreadimplementsRunnable{
publicstaticfinalintThread.MAX_PRIORITY;
publicstaticfinalintThread.MIN_PRIORITY;
publicstaticfinalintThread.NORM_PRIORITY;
publicvoidsetPriority(intpriority);
publicintgetPriority();
}
ThesetPriority()methodchangesthepriorityofaparticularthread.Thismethodcanbecalledatanytime(subjecttosecurityrestrictions,whichwe
discussinChapter13).Aswe'llseelaterinthischapter,usingprioritiestogivepreferencetocertainthreadsmayormaynotgiveyoutheeffectyouexpect.In
general,attemptingtoinfluenceschedulingbehaviorusingprioritiesofferslimitedbenefit.
IntheJavaThreadclass,threestaticfinalvariablesdefinetheallowablerangeofthreadpriorities:
Thread.MIN_PRIORITY
Theminimumpriorityathreadcanhave(althoughthevirtualmachineisallowedtohavelowerprioritythreadsthanthisone)
Thread.MAX_PRIORITY
Themaximumpriorityathreadcanhave
Thread.NORM_PRIORITY
Thedefaultpriorityforathread
Thesymbolicdefinitionofpriorityconstantsisnotnecessarilyuseful.Typically,weliketothinkofconstantvaluesliketheseintermsofsymbolicnames,
whichallowsustobelievethattheactualvaluesareirrelevant.Usingsymbolicnamesalsoallowsustochangethevariablesandhavethatchangereflected
throughoutourcode.
Unfortunately,thatlogicdoesn'tapplyinthecaseofthreadpriorities:ifwehavetomanipulatetheindividualprioritiesofthreads,wesometimeshavetoknow
whattherangeofthosevaluesactuallyis.Becauseofthewayinwhichthesevaluesmaptothreadprioritiesofoperatingsystems,threadswithdifferentJava
prioritiesmayendupwiththesameoperatingsystempriority.Whenyouwriteanapplet,thethreadthattheappletrunsinisgivenapriorityof
NORM_PRIORITY+1.It'sinterestingtowonderhowfaryoucantakethis:NORM_PRIORITY+2,+3,andsoonuntilyougettoMAX_PRIORITY.If
youreallywanttoworkwithJava'sfullrangeofpriorities,thesymbolicvaluesdon'thelpyou:youhavetoknowthattheminimumpriorityavailableto
developersis1,themaximumis10,andthedefaultis5.Thisyields10distinctprioritiesthatyoucanassigntoaathreadthe11thpriority(priority0)is
reservedforthevirtualmachine.
Ontheotherhand,notalloperatingsystemssupport10distinctlevelsofthreadpriorities,soNORM_PRIORITY2andNORM_PRIORITY3maybe
thesamethingonyourparticularmachine.Workingwithnumericvaluesdoesn'treallyprovideafullrangeeither.Thebestwecandoforportable
applicationsistousethethreesymbolicprioritiesandrealizethatthey'rereallyjustahinttothevirtualmachineanyway.
Let'sseewhathappenswhenweusethesecalls.We'llmodifyourFibonaccicalculatorsothateachofthetaskthreadsisstartedwithadifferentpriority:
for(inti=0;i<t.length;i++){
t[i]=newThread(newTask(n,"Task"+i));
t[i].setPriority((i%10)+1);
t[i].start();
}
Whathappenswhenwerunthisprogramisverydependentontheoperatingsystemhostingtheprogram.We'lldiscussthateffectforseveralpopular
platformsinthenextsection.
we'drathernothavetowrite:Javaisaplatformindependentlanguageandtohavetoprovideplatformspecificdetailsofitsimplementationscertainlyviolates
thatprecept.Butwestressthatthesedetailsactuallymatterinveryfewcases.Thissectionisstrictlyforinformationalpurposes.
Green Threads
Thefirstmodelthatwe'lllookatisthesimplest.Inthismodel,theoperatingsystemdoesn'tknowanythingaboutJavathreadsatallitisuptothevirtual
machinetohandleallthedetailsofthethreadingAPI.Fromtheperspectiveoftheoperatingsystem,thereisasingleprocessandasinglethread.
Eachthreadinthismodelisanabstractionwithinthevirtualmachine:thevirtualmachinemustholdwithinthethreadobjectallinformationrelatedtothat
thread.Thisincludesthethread'sstack,aprogramcounterthatindicateswhichJavainstructionthethreadisexecuting,andotherbookkeepinginformation
aboutthethread.Thevirtualmachineisthenresponsibleforswitchingthreadcontexts:thatis,savingthisinformationforoneparticularthread,loadingit
fromanotherthread,andthenexecutingthenewthread.Asfarastheoperatingsystemisconcerned,thevirtualmachineisjustexecutingarbitrarycodethe
factthatthecodeisemulatingmanydifferentthreadsisunknownoutsideofthevirtualmachine.
ThismodelisknowninJavaasthegreenthreadmodel.Inothercircles,thesethreadsareoftencalleduserlevelthreadsbecausetheyexistonlywithinthe
userleveloftheapplication:nocallsintotheoperatingsystemarerequiredtohandleanythreaddetails.
IntheearlydaysofJava,thegreenthreadmodelwasfairlycommon,particularlyonmostUnixplatforms.Somespecializedoperatingsystemstodayusethis
model,butmostcomputersuseanative,systemlevelmodel.
Thegreenthreadmodeliscompletelydeterministicwithrespecttoscheduling.Runningourprioritycalculationabove,weseethisoutput:
StartingtaskTask5at07:23:12:074
EndingtaskTask5at07:23:12:995after921milliseconds
StartingtaskTask4at07:23:13:111
StartingtaskTask6at07:23:13:281
EndingtaskTask6at07:23:14:256after975milliseconds
StartingtaskTask7at07:23:14:386
EndingtaskTask7at07:23:15:398after1012milliseconds
StartingtaskTask8at07:23:15:504
EndingtaskTask8at07:23:16:567after963milliseconds
StartingtaskTask9at07:23:16:624
EndingtaskTask9at07:23:17:699after1075milliseconds
EndingtaskTask4at07:23:18:912after5801milliseconds
StartingtaskTask3at07:23:19:114
EndingtaskTask3at07:23:20:177after1063milliseconds
StartingtaskTask2at07:23:20:301
EndingtaskTask2at07:23:21:305after1004milliseconds
StartingtaskTask1at07:23:21:486
EndingtaskTask1at07:23:22:449after963milliseconds
Assoonasthethreadwithpriority6(task5)becomesrunnable,thegreenthreadschedulerrunsit,andallthreadsmustwait.Thatincludesthemainthread,
whichcannotgoontocreateahigherprioritythread.Thisiswhythepriority9threadrunsafterthepriority68threadshavefinished:themainthreadcannot
createthepriority9threadbecauseitrunsatapriorityof5andisblockedbythethreadsatpriority68.Task4getstorunoccasionallywhenthemainthread
isblocked,anditeventuallycompletesafterveryhighprioritytask9.
Table91.MappingofJavathreadprioritiesonWin32platforms
Javapriority
Win32priority
THREAD_PRIORITY_IDLE
1(Thread.MIN_PRIORITY)
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_BELOW_NORMAL
5(Thread.NORM_PRIORITY)
THREAD_PRIORITY_NORMAL
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_HIGHEST
THREAD_PRIORITY_HIGHEST
10(Thread.MAX_PRIORITY)
THREAD_PRIORITY_TIME_CRITICAL
Onthisimplementation,athreadwithaJavapriorityof3andonewithaJavapriorityof4havethesameeffectivepriority.
Inadditionto7prioritylevels,Windowsoperatingsystemsalsohave5schedulingclasses,andathreadisactuallyscheduledasacombinationofitspriority
anditsschedulingclass.However,schedulingclassesarenoteasytochange,sotheydonotfactorintoadiscussionofJavathreads.
Windowsoperatingsystemsalsouseacomplexprioritycalculationthatincludesthefollowing:
Threadsaresubjecttopriorityinheritance.
Theactualpriorityofathreadisbasedonitsprogrammed(orinverted)priorityminusavaluethatindicateshowrecentlythethreadhasactuallyrun.This
valueissubjecttocontinualadjustment:themoretimepasses,theclosertozerothatvaluebecomes.Thisprimarilydistinguishesbetweenthreadsofthe
samepriority,anditleadstoroundrobinschedulingofthreadsofthesamepriority.
Onanotherlevel,athreadthathasnotrunforaverylongtimeisgivenatemporarypriorityboost.Thevalueofthisboostdecaysovertimeasthethread
hasachancetorun.Thispreventsthreadsfromabsolutestarvationwhilestillgivingpreferencetohigherprioritythreadsoverlowerprioritythreads.The
effectofthispriorityboostdependsontheoriginalpriorityofthethread.
Threadsrunninginaprogramthathaskeyboardandmousefocusaregivenapriorityboostoverthreadsinotherprograms.
Theupshotofallthisisthatit'sverydifficulttoguaranteeexplicitlyorderedthreadexecutiononWindowsplatforms,butthecomplexprioritycalculation
ensuresthatthreadsdonotstarve.
OnWindowsoperatingsystems,theoutputofourprioritybasedthreadcalculationlookssomethinglikethis:
StartingtaskTask9at21:19:23:590
StartingtaskTask8at21:19:23:590
StartingtaskTask7at21:19:23:590
EndingtaskTask9at21:19:28:750after5160milliseconds
StartingtaskTask4at21:19:29:470
EndingtaskTask8at21:19:30:180after6590milliseconds
StartingtaskTask2at21:19:30:180
StartingtaskTask0at21:19:30:460
EndingtaskTask7at21:19:32:050after8460milliseconds
StartingtaskTask6at21:19:23:590
StartingtaskTask5at21:19:23:590
StartingtaskTask3at21:19:30:180
EndingtaskTask5at21:19:35:950after12360milliseconds
EndingtaskTask6at21:19:35:950after12360milliseconds
StartingtaskTask1at21:19:30:180
EndingtaskTask4at21:19:37:820after8350milliseconds
EndingtaskTask2at21:19:41:610after11430milliseconds
EndingtaskTask3at21:19:41:720after11540milliseconds
EndingtaskTask0at21:19:45:120after14660milliseconds
EndingtaskTask1at21:19:45:120after14940milliseconds
Onthisplatform,thecomplexprioritycalculationplacesagreatdealofemphasisontheJavaprioritylevel.Infact,thehighestprioritytasksfinishbefore
someofthelowerprioritytasksevenhaveachancetostart.NotealsothatseveralJavaprioritylevelsmaptothesameWindowsprioritylevel:priorities6
and7(tasks5and6)aregiventhesameprioritybytheoperatingsystem,asarepriorities1and2(tasks0and1).
StartingtaskTask7at21:26:33:040
StartingtaskTask0at21:26:33:040
StartingtaskTask6at21:26:33:039
StartingtaskTask9at21:26:33:039
StartingtaskTask4at21:26:33:039
StartingtaskTask2at21:26:33:040
StartingtaskTask5at21:26:33:039
StartingtaskTask3at21:26:33:039
StartingtaskTask8at21:26:33:039
StartingtaskTask1at21:26:33:039
EndingtaskTask6at21:27:02:580after29541milliseconds
EndingtaskTask1at21:27:02:802after29763milliseconds
EndingtaskTask4at21:27:03:618after30579milliseconds
EndingtaskTask7at21:27:04:173after31133milliseconds
EndingtaskTask0at21:27:04:259after31219milliseconds
EndingtaskTask9at21:27:04:375after31336milliseconds
EndingtaskTask3at21:27:04:457after31418milliseconds
EndingtaskTask5at21:27:05:050after32011milliseconds
EndingtaskTask8at21:27:05:159after32120milliseconds
EndingtaskTask2at21:27:05:287after32247milliseconds
Thelowerprioritythreadstendtostartlaterthanthehigherprioritythreads,butpriorityisnoassuranceofmoreCPUtime:thethreadatpriority8finishes
laterthanalmostanyotherthread.Thecomplexprioritycalculationbeingperformedbytheoperatingsystemensuresthatallthreadsgetadequateamountsof
CPUtime.
Atanapplicationlevel,threadsonSolariscanhaveanyof128priorities(though,aswementioned,thatpriorityisfactoredintoacomplexequationthatyields
60differentrunnablepriorities).Theseprioritiesrunfrom0to127,andinCandC++programs,thedefaultpriorityforathreadis127.InJavaversionsup
toandincludingJDK1.4,Javathreadprioritiesweremappedtothefullrangeof128priorities(0,12,24,andsoon).Thismeantthatthedefaultpriorityfora
JavathreadwasinthemiddleofthisrangeandhencelessthanthedefaultpriorityforaCorC++thread.WhenaSolarismachineranaCPUintensiveC
programalongwithaCPUintensiveJavaprogram,theJavaprogramwasatadisadvantageandreceivedlessthan50%oftheavailableCPUtime.
InJ2SE5.0,themappingwaschangedandallJavathreadswithapriorityofNORM_PRIORITYandhigherarenowmappedtoaSolaristhreadpriorityof
127.ThisallowsJavaandCprogramstorunatparity.
Summary
ThreadschedulingisagrayareaofJavaprogrammingbecauseactualschedulingmodelsarenotdefinedbytheJavaspecification.Asaresult,scheduling
behaviorcan(anddoes)varyondifferentmachines.
Inageneralsense,threadshaveapriority,andthreadswithahigherprioritytendtorunmoreoftenthatthreadswithalowerpriority.Thedegreetowhichthis
istruedependsontheunderlyingoperatingsystemWindowsoperatingsystemsgivemoreprecedencetothethreadprioritywhileUnixstyleoperating
systemsgivemoreprecedencetolettingallthreadshaveasignificantamountofCPUtime.
Forthemostpart,thisthreadschedulingdoesn'tmatter:theinformationwe'velookedatinthischapterisimportantforunderstandingwhat'sgoingoninyour
program,butthere'snotmuchyoucandotochangethewayitworks.Inthenexttwochapters,we'lllookatotherkindsofthreadschedulingand,usingthe
informationwe'vejustlearned,seehowtomakeoptimaluseofmultiplethreadsonmultipleCPUs.
Example Classes
HereistheclassnameandAnttargetfortheexampleinthischapter:
Description
MainJavaclass
Anttarget
RecursiveFibonacciCalculator
javathreads.examples.ch09.example1.ThreadTestnThreadsFibCalcValue
ch9ex1
TheFibonaccitestrequirescommandlineargumentsthatspecifythenumberofthreadstorunsimultaneouslyandthevaluetocalculate.IntheAntscript,
thoseargumentsaredefinedbytheseproperties:
<propertyname="nThreads"value="10"/>
<propertyname="FibCalcValue"value="20"/>
Chapter10.Thread Pools
Forvariousreasons,threadpoolsareaverycommontoolinamultithreadeddeveloper'stoolkit.Mostprogramsthatusealotofthreadsbenefitinsomeway
fromusingathreadpool.
J2SE5.0comeswithitsownthreadpoolimplementation.Priortothisrelease,developerswerelefttowritetheirownthreadpooloruseanynumberof
commonlyavailableimplementations(includingonewedevelopedinearliereditionsofthisbookandwhichisdiscussedinAppendixA).Inthischapter,we
discussthethreadpoolimplementationthatcomeswithJ2SE5.0.Ifyoucan'tusethatimplementationyet,theinformationinthischapterisstilluseful:you'll
findouthowandwhenusingathreadpoolcanbeadvantageous.Withthatunderstanding,it'ssimpletouseanythreadpoolimplementationinyourown
program.
StartingtaskTask2at00:04:30:324
StartingtaskTask0at00:04:30:334
StartingtaskTask1at00:04:30:345
EndingtaskTask1at00:04:38:052after7707milliseconds
EndingtaskTask2at00:04:38:380after8056milliseconds
EndingtaskTask0at00:04:38:502after8168milliseconds
Inthiscase,wehavethreethreadsandoneCPU.Thethreethreadsrunatthesametime,aretimeslicedbytheoperatingsystem,andallcompletedexecution
inaroundeightseconds.Imaginethatwehavewrittenthisprogramasaserverwhereeachtimeaclientconnects,itisgivenaseparatethread.Whenthethree
clientseachrequesttheservice(thatis,thecalculationoftheFibonaccinumber),eachwillwaiteightsecondsforitsanswer.
Inoursecondexample,werunthethreadssequentiallyandseethisoutput:
StartingtaskTask0at00:04:30:324
EndingtaskTask0at00:04:33:052after2728milliseconds
StartingtaskTask1at00:04:33:062
EndingtaskTask1at00:04:35:919after2857milliseconds
StartingtaskTask2at00:04:35:929
EndingtaskTask2at00:04:38:720after2791milliseconds
Inthiscase,thetotaltimetocompletethecalculationisstillabout8seconds,buteachthreadcompletesitsexecutioninabout2.7seconds.Aserverthatruns
thecalculationssequentiallywillprovideitsfirstanswerin2.7seconds,andtheaveragewaitingtimefortheclientswillbe5.4seconds.
Thisiswhatwemeanbythethroughputoftheprogram.Inbothcases,we'vedonethesameamountofwork,butinthesecondcase,usersoftheprogram
aregenerallyhappierwiththeperformance.
Nowconsiderwhathappensifadditionalrequestscomeinwhiletheserverisexecuting.Ifwecreateanewthreadforeveryclient,theservercouldquickly
becomeoverloaded:themorethreadsitstarts,thesloweritprovidesananswerforeachrequest.Withthreesimultaneousthreads,ourcalculationtakeseight
seconds.Ifanewrequestarrivesevery2.7secondsorso,weneverfinish.Theserverstartsmoreandmorethreads,eachthreadgetslessandlessCPUtime,
andnoneeverfinish.
Ontheotherhand,ifweruntherequestssequentiallyusingonlyonethread,theserverreachesasteadystate.Withthreerequestsinthequeue,each
subsequentrequestarrivesasanotheronefinishes.Wecansupplyanendlessnumberofanswerstotheclientseachclientwaitsabouteightsecondsfora
response.
Thisreasoningappliestoprogramsotherthanservers.Forinstance,animageprocessingapplicationmaynicelypartitionitsimageandbeabletoworkon
eachpartitioninaseparatethread.Ifauseriswatchingtheimageonscreen,youmightwanttodisplaytheresultsofonepartitionwhileanotheroneisbeing
manipulated.
Thesimilaritytoprogramslikethisandserversisthattheresultsofeachthreadareinteresting.Theresultofasinglecalculationisinterestingtotheclientthat
requestedit,theresultofapartitionoftheimageisinterestingtotheuserviewingthescreen,andsoon.Inthesecases,throttlingthenumberofthreads
providesabetterexperiencefortheusersoftheapplication.
Clearly,partsofthisdiscussionarecontrivedwe'veselectedthenumbersinthebestwaypossibletomakeourpoint,andwe'veusedacalculationthatneeds
onlyCPUresourcestocomplete.Intherealworld,requestsarriveattheserverinrandombursts,andprocessingtherequestinvolvesmakingdatabasecalls
orsomethingelsethatislikelytoblock.Thosethingscomplicateusingathreadpool,buttheydonoteliminateitsbenefits.
ThefactthatthreadsmayblockmeansthatweneedtohavemorethreadsthanCPUsinourpool.Sofar,we'veconsideredcaseswherethereisoneCPUand
haveseenthatoneCPUintensivethreadgivesusthebestthroughput.Ifthethreadspends50%ofitstimeblocked,youwanttwothreadsperCPUifthe
threadblocks66%ofthetime,youwantthreethreadsperCPU,andsoon.
Ofcourse,you'reunlikelytobeabletomodelyourprograminsuchdetail.Andanymodelbecomesfarhardertocalculateonceyoustarttoaccountfor
randomburstsintraffic.Intheend,you'llneedtorunsometeststodetermineanappropriatesizeforyourthreadpool.ButifCPUresourcesaresometimes
scarce,throttlingthenumberofthreads(whilestillkeepingtheCPUsutilized)increasesthethroughputofyourapplication.
Executors
Java'simplementationofthreadpoolsisbasedonanexecutor.Anexecutorisagenericconceptmodelledbythisinterface:
packagejava.util.concurrent;
publicinterfaceExecutor{
publicvoidexecute(Runnabletask);
}
Executorsareausefuldesignpatternformultithreadedprogramsbecausetheyallowyoutomodelyourprogramasaseriesoftasks.Youdon'tneedtoworry
aboutthethreaddetailsassociatedwiththetask:yousimplycreatethetaskandpassittotheexecute()methodofanappropriateexecutor.
J2SE5.0comeswithtwokindsofexecutors.Itcomeswithathreadpoolexecutor,whichwe'llshownext.Italsoprovidesataskschedulingexecutor,which
weexamineinChapter11.Bothoftheseexecutorsaredefinedbythisinterface:
packagejava.util.concurrent;
publicinterfaceExecutorServiceextendsExecutor{
voidshutdown();
ListshutdownNow();
booleanisShutdown();
booleanisTerminated();
booleanawaitTermination(longtimeout,TimeUnitunit)
throwsInterruptedException;
<T>Future<T>submit(Callable<T>task);
<T>Future<T>submit(Runnabletask,Tresult);
Future<?>submit(Runnabletask);
<T>List<Future<T>>invokeAll(Collection<Callable<T>>tasks)
throwsInterruptedException;
<T>List<Future<T>>invokeAll(Collection<Callable<T>>tasks,
longtimeout,TimeUnitunit)
throwsInterruptedException;
<T>TinvokeAny(Collection<Callable<T>>tasks)
throwsInterruptedException,ExecutionException;
<T>TinvokeAny(Collection<Callable<T>>tasks,longtimeout,TimeUnitunit)
throwsInterruptedException,ExecutionException,TimeoutException;
}
Thisinterfaceprovidesameansforyoutomanagetheexecutoranditstasks.Theshutdown()methodgracefullyterminatestheexecutor:anytasksthat
havealreadybeensenttotheexecutorareallowedtorun,butnonewtasksareaccepted.Whenalltasksarecompleted,theexecutorstopsitsthread(s).The
shutdownNow()methodattemptstostopexecutionsooner:alltasksthathavenotyetstartedarenotrunandareinsteadreturnedinalist.Still,existingtasks
continuetorun:theyareinterrupted,butit'suptotherunnableobjecttocheckitsinterruptstatusandexitwhenconvenient.
Sothere'saperiodoftimebetweencallingtheshutdown()orshutdownNow()methodandwhentasksexecutingintheexecutorserviceareallcomplete.
Whenalltasksarecomplete(includinganywaitingtasks),theexecutorserviceentersaterminatedstate.Youcanchecktoseeiftheexecutorserviceisinthe
terminatedstatebycallingtheisTerminated()method(oryoucanwaitforittofinishthependingtasksbycallingtheawaitTerminated()method).
AnexecutorservicealsoallowsyoutohandlemanytasksinwaysthatthesimpleExecutorinterfacedoesnotaccommodate.Taskscanbesenttoan
executorserviceviaasubmit()method,whichreturnsaFutureobjectthatcanbeusedtotracktheprogressofthetask.TheinvokeAll()methods
executeallthetasksinthegivencollection.TheinvokeAny()methodsexecutethetasksinthegivencollection,butwhenonetaskhascompleted,the
remainingtasksaresubjecttocancellation.We'lldiscussFutureobjectsandcancellationlaterinthischapter.
packagejava.util.concurrent;
publicclassThreadPoolExecutorimplementsExecutorService{
publicThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue);
publicThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue,
ThreadFactorythreadFactory);
publicThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue,
RejectedExecutionHandlerhandler);
publicThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue,
ThreadFactorythreadFactory,
RejectedExecutionHandlerhandler);
}
Thecorepoolsize,maximumpoolsize,keepalivetimes,andsooncontrolhowthethreadswithinthepoolaremanaged.Wedescribeeachoftheseconcepts
inournextsection.
Fornow,wecanuseaconstructortocreatethetasksandputtheminthethreadpool:
packagejavathreads.examples.ch10.example1;
importjava.util.concurrent.*;
importjavathreads.examples.ch10.*;
publicclassThreadPoolTest{
publicstaticvoidmain(String[]args){
intnTasks=Integer.parseInt(args[0]);
longn=Long.parseLong(args[1]);
inttpSize=Integer.parseInt(args[2]);
ThreadPoolExecutortpe=newThreadPoolExecutor(
tpSize,tpSize,50000L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue<Runnable>());
Task[]tasks=newTask[nTasks];
for(inti=0;i<nTasks;i++){
tasks[i]=newTask(n,"Task"+i);
tpe.execute(tasks[i]);
}
tpe.shutdown();
}
}
Inthisexample,we'reusingthetaskstocalculateFibonaccinumbersaswedoinChapter9.Oncethepoolisconstructed,wesimplyaddthetaskstoit(using
theexecute()method).Whenwe'redone,wegracefullyshutdownthepooltheexistingtasksruntocompletion,andthenalltheexistingthreadsexit.As
youcansee,usingthethreadpoolisquitesimple,butthebehaviorofthepoolcanbecomplexdependingontheargumentsusedtoconstructit.We'lllook
intothatinthenextsection.
publicintgetActiveCount();
publicBlockingQueue<Runnable>getQueue();
publiclonggetTaskCount();
publiclonggetCompletedTaskCount();
}
Thefirstsetofmethodsdealwiththethreadpool'ssize,andtheremainingmethodsdealwithitsqueue.
Size
Thesizeofthethreadpoolvariesbetweenagivenminimum(orcore)andmaximumnumberofthreads.Inourexample,weusethesameparameterfor
bothvalues,makingthethreadpoolaconstantsize.
Ifyouspecifydifferentnumbersfortheminimumandmaximumnumberofthreads,thethreadpooldynamicallyaltersthenumberofthreadsitusesto
runitstasks.Thecurrentsize(returnedfromthegetPoolSize()method)fallsbetweenthecoresizeandthemaximumsize.
Queue
Thequeueisthedatastructureusedtoholdtasksthatareawaitingexecution.Thechoiceofqueueaffectshowcertaintasksarescheduled.Inthiscase,
we'veusedalinkedblockingqueue,whichplacestheleastconstraintsonhowtasksareaddedtothequeue.Onceyou'vepassedthisqueuetothethread
pool,youshouldnotcallanymethodsonitdirectly.Inparticular,donotadditemsdirectlytothequeueaddthemthroughtheexecute()methodofthe
threadpool.ThegetQueue()methodreturnsthequeue,butyoushouldusethatfordebuggingpurposesonlydon'texecutemethodsdirectlyonthe
queueortheinternalworkingsofthethreadpoolbecomeconfused.
Theseparametersallowconsiderableflexibilityinthewaythethreadpooloperates.Thebasicprincipleisthatthethreadpooltriestokeepitsminimum
numberofthreadsactive.Ifitgetstoobusy(wherebusyisapropertyoftheparticularqueuethatthethreadpooluses),itaddsthreadsuntilthemaximum
numberofthreadsisreached,atwhichpointitdoesnotallowanymoretaskstobequeued.
Therearesomenuancesinthis,particularlyinhowthequeueinteractswiththenumberofthreads.Let'stakeitstepbystep:
1.ThethreadpoolisconstructedwithMcorethreadsandNmaximumthreads.Atthispoint,nothreadsareactuallycreated(thoughyoucanspecifythat
thepoolcreatetheMcorethreadsbycallingthethreadpool'sprestartAllCoreThreads()methodorthatitpreallocateonecorethreadbycalling
theprestartCoreThread()method).
2.Ataskentersthepool(viathethreadpool'sexecute()method).Nowoneoffivethingshappens:
1.IfthepoolhascreatedfewerthanMthreads,itstartsanewthreadandrunsthenewtaskimmediately.Evenifsomeoftheexistingthreadsareidle,a
newthreadiscreatedinthepool'sattempttoreachMthreads.
2.IfthepoolhasbetweenMandNthreadsandoneofthosethreadsisidle,thetaskisrunbyanidlethread.
3.IfthepoolhasbetweenMandNthreadsandallthethreadsarebusy,thethreadpoolexaminestheexistingworkqueue.Ifthetaskcanbeplacedon
theworkqueuewithoutblocking,it'sputonthequeueandnonewthreadisstarted.
4.IfthepoolhasbetweenMandNthreads,allthreadsarebusy,andthetaskcannotbeaddedtothequeuewithoutblocking,thepoolstartsanew
threadandrunsthetaskonthatthread.
5.IfthepoolhasNthreadsandallthreadsarebusy,thepoolattemptstoplacethenewtaskonthequeue.Ifthequeuehasreacheditsmaximumsize,
thisattemptfailsandthetaskisrejected.Otherwise,thetaskisacceptedandrunwhenathreadbecomesidle(andallpreviouslyqueuedtaskshave
run).
3.Ataskcompletesexecution.Thethreadrunningthetaskthenrunsthenexttaskonthequeue.Ifnotasksareonthequeue,oneoftwothingshappens:
1.IfthepoolhasmorethanMthreads,thethreadwaitsforanewtasktobequeued.Ifanewtaskisqueuedwithinthetimeoutperiod,thethreadruns
it.Ifnot,thethreadexits,reducingthetotalnumberofthreadsinthepool.Thetimeoutperiodisaparameterusedtoconstructthethreadpoolinour
example,wespecified50seconds(50000LtimeunitsofTimeUnit.MILLISECONDS).Notethatifthespecifiedtimeoutis0,thethreadalways
exits,regardlessoftherequestedminimumthreadpoolsize.
2.IfthepoolhasMorfewerthreads,thethreadblocksindefinitelywaitingforanewtasktobequeued(unlessthetimeoutwas0,inwhichcaseit
exits).Itrunsthenewtaskwhenavailable.
Whataretheimplicationsofallthis?Itmeansthatthechoiceofpoolsizeandqueueareimportanttogettingthebehavioryouwant.Foraqueue,you
havethreechoices:
1.ASynchronousQueue,whicheffectivelyhasasizeof0.Inthiscase,wheneverthepooltriestoqueueatask,itfails.Theimplicationofthisis
tasksareeitherrunimmediately(becausethepoolhasanidlethreadorisbelowitsthresholdand,therefore,createsanewthread)orarerejected
immediately.Notethatyoucanpreventrejectionofataskifyouspecifyanunlimitedmaximumnumberofthreads,butthispreventsthethrottling
benefitofusingathreadpoolinthefirstplace.
2.Anunboundedqueue,suchasaLinkedBlockingQueuewithanunlimitedcapacity.Inthiscase,addingatasktothequeuealwayssucceeds,
whichmeansthatthethreadpoolnevercreatesmorethanMthreadsandneverrejectsatask.
3.Aboundedqueue,suchasaLinkedBlockingQueuewithafixedcapacityoranArrayBlockingQueue.Let'ssupposethatthequeuehasa
boundsofP.Astasksareaddedtothepool,itcreatesthreadsuntilitreachesMthreads.Atthatpoint,itstartsqueueingtasksuntilthenumberof
waitingtasksreachesP.Asmoretasksareadded,thepoolstartsaddingthreadsuntilitreachesNthreads.IfwereachastatewhereNthreadsare
activeandPtasksarequeued,additionaltasksarerejected.
Inourexample,weusedaLinkedBlockingQueuewithanunboundedcapacityandafixedpoolsize.Thisisperhapsthemostcommonconfigurationof
threadpools:itallowstaskstowaitforanavailablethread,andafixednumberofthreadsiseasiertomonitorthanavariablenumberofthreads.Agood
alternativetothisistouseaboundedqueuewithafixednumberofthreads.Inthismodel,iftasksstarttoarrivefasterthantheycanbeprocessed,theyqueue.
Unliketheunboundedcase,however,atsomepointthequeuethresholdisreachedandyourprogrammusttakeappropriateaction:ifit'saserver,itcanreject
futurerequestsfromclients,tellingthemthatit'stoobusyrightnowandtheyshouldtryagainlater.
Ifyouuseathreadpool,thereisnomagicformulathatyoucanusetodetermineitsoptimalsizeandqueuingstrategy.WhentheoperationsarestrictlyCPU
bound,useonlyasmanythreadsasthereareCPUs.Formorecomplexoperations,choosingathreadpoolsizeisamatteroftestingdifferentvaluestosee
whichgivesyouthebestprogramperformance.
Rejected Tasks
Dependingonthetypeofqueueyouuseinthethreadpool,ataskmayberejectedbytheexecute()method.Tasksarerejectedifthequeueisfullorifthe
shutdown()methodhasbeencalledonthethreadpool.
Whenataskisrejected,thethreadpoolcallstherejectedexecutionhandlerassociatedwiththethreadpool.TheseAPIsdealwiththerejectedexecution
handler:
packagejava.util.concurrent;
publicinterfaceRejectedExecutionHandler{
publicvoidrejectedExecution(Runnabler,ThreadPoolExecutorexecutor);
}
packagejava.util.concurrent;
publicclassThreadPoolExecutorimplementsExecutorService{
publicvoidsetRejectedExecutionHandler(RejectedExecutionHandlerhandler);
publicRejectedExecutionHandlergetRejectedExecutionHandler();
publicstaticclassAbortPolicyimplementsRejectedExecutionHandler;
publicstaticclassCallerRunsPolicyimplementsRejectedExecutionHandler;
publicstaticclassDiscardPolicyimplementsRejectedExecutionHandler;
publicstaticclassDiscardOldestPolicyimplementsRejectedExecutionHandler;
}
Thereisonerejectedexecutionhandlerfortheentirepoolitappliestoallpotentialtasks.Youcanwriteyourownrejectedexecutionhandler,oryoucanuse
oneoffourpredefinedhandlers.Bychoosingapredefinedrejectedexecutionhandlerorbycreatingyourownhandleryourprogramcantakeappropriate
actionwhenataskisrejected.
Herearethepredefinedhandlers:
AbortPolicy
Thishandlerdoesnotallowthenewtasktobescheduledwhenthequeueisfull(orthepoolhasbeenshutdown)inthatcase,theexecute()method
throwsaRejectedExecutionException.Thatexceptionisaruntimeexception,sowhenusingthispolicy,it'suptotheprogramtocatchthe
exception.Otherwise,theexceptionispropagatedupthestack.
Thisisthedefaultpolicyforrejectedtasks.
CallerRunsPolicy
Thishandlerexecutesthenewtaskindependentlyofthethreadpoolifthequeueisfull.Thatis,ratherthanqueuingthetaskandexecutingitinanother
thread,thetaskisimmediatelyexecutedbycallingitsrun()method,andtheexecute()methoddoesnotreturnuntilthetaskhascompleted.Ifthe
taskisrejectedbecausethepoolhasbeenshutdown,thetaskissilentlydiscarded.
DiscardPolicy
Thishandlersilentlydiscardsthetask.Noexceptionisthrown.
DiscardOldestPolicy
Thishandlersilentlydiscardstheoldesttaskinthequeueandthenqueuesthenewtask.WhenusedwithaLinkedBlockingQueueor
ArrayBlockingQueue,theoldesttaskistheonethatisfirstinlinetoexecutewhenathreadbecomesidle.WhenusedwithaSynchronousQueue,
thereareneverwaitingtasksandsotheexecute()methodsilentlydiscardsthesubmittedtask.
Ifthepoolhasbeenshutdown,thetaskissilentlydiscarded.
Tocreateyourownrejectedtaskhandler,createaclassthatimplementstheRejectedExecutionHandlerinterface.Yourhandler(justlikeapredefined
handler)canthenbesetusingthesetRejectedExecutionHandler()methodofthethreadpoolexecutor.
Thread Creation
Thethreadpooldynamicallycreatesthreadsaccordingtothesizepoliciesineffectwhenataskisqueuedandterminatesthreadswhenthey'vebeenidletoo
long.Thosepoliciesaresetwhenthepoolisconstructed,andtheycanbealteredwiththesemethods:
packagejava.util.concurrent;
publicinterfaceThreadFactory{
publicThreadnewThread(Runnabler);
}
packagejava.util.concurrent;
publicclassThreadPoolExecutorimplementsExecutorService{
publicvoidsetThreadFactory(ThreadFactorythreadFactory);
publicThreadFactorygetThreadFactory();
publicvoidsetKeepAliveTime(longtime,TimeUnitunit);
publiclonggetKeepAliveTime(TimeUnitunit);
}
Whenthepoolcreatesathread,itusesthecurrentlyinstalledthreadpoolfactorytodoso.Creatingandinstallingyourownthreadfactoryallowsyoutosetup
acustomschemetocreatethreadssothattheyarecreatedwithspecialnames,priorities,daemonstatus,threadgroup,andsoon.
Thedefaultthreadfactorycreatesathreadwiththefollowingcharacteristics:
Newthreadsbelongtothesamethreadgroupasthethreadthatcreatedtheexecutor.However,thesecuritymanagerpolicycanoverridethisandplacethe
newthreadinitsownthreadgroup(seeChapter13).
Thenameofthethreadreflectsitspoolnumberanditsthreadnumberwithinthepool.Withinapool,threadsarenumberedconsecutivelybeginningwith
1threadpoolsaregloballyassignedapoolnumberconsecutivelybeginningwith1.
Thedaemonstatusofthethreadisthesameasthestatusofthethreadthatcreatedtheexecutor.
ThepriorityofthethreadisThread.NORM_PRIORITY.
Unlikearunnableobject,acallableobjectcanreturnaresultorthrowacheckedexception.Callableobjectsareusedonlybyexecutorservices(notsimple
executors)theservicesoperateoncallableobjectsbyinvokingtheircall()methodandkeepingtrackoftheresultsofthosecalls.
Whenyouaskanexecutorservicetorunacallableobject,theservicereturnsaFutureobjectthatallowsyoutoretrievethoseresults,monitorthestatusof
thetask,andcancelthetask.TheFutureinterfacelookslikethis:
publicinterfaceFuture<V>{
Vget()throwsInterruptedException,ExecutionException;
Vget(longtimeout,TimeUnitunit)
throwsInterruptedException,ExecutionException,TimeoutException;
booleanisDone();
booleancancel(booleanmayInterruptIfRunning);
booleanisCancelled();
}
Callableandfutureobjectshaveaonetoonecorrespondence:everycallableobjectthatissenttoanexecutorservicereturnsamatchingfutureobject.The
get()methodofthefutureobjectreturnstheresultsofitscorrespondingcall()method.Theget()methodblocksuntilthecall()methodhas
returned(oruntiltheoptionaltimeouthasexpired).Ifthecall()methodthrowsanexception,theget()methodthrowsanExecutionExceptionwith
anembeddedcause,whichistheexceptionthrownbythecall()method.
ThefutureobjectkeepstrackofthestateofanembeddedCallableobject.Thestateissettocancelledwhenthecancel()methodiscalled.Whenthe
call()methodofacallabletaskiscalled,thecall()methodchecksthestate:ifthestateiscancelled,thecall()methodimmediatelyreturns.
Whenthecancel()methodiscalled,thecorrespondingcallableobjectmaybeinoneofthreestates.Itmaybewaitingforexecution,inwhichcaseitsstate
issettocancelledandthecall()methodisneverexecuted.Itmayhavecompletedexecution,inwhichcasethecancel()methodhasnoeffect.The
objectmaybeintheprocessofrunning.Inthatcase,ifthemayInterruptIfRunningflagisfalse,thecancel()methodagainhasnoeffect.
IfthemayInterruptIfRunningflagistrue,however,thethreadrunningthecallableobjectisinterrupted.Thecallableobjectmuststillpayattentionto
this,periodicallycallingtheThread.interrupted()methodtoseeifitshouldexit.
Whenanobjectinathreadpooliscancelled,thereisnoimmediateeffect:theobjectstillremainsqueuedforexecution.Whenthethreadpoolisaboutto
executetheobject,itcheckstheobject'sinternalstate,seesthatithasbeencancelled,andskipsexecutionoftheobject.So,cancellinganobjectonathread
poolqueuedoesnotimmediatelymakespaceinthethreadpool'squeue.Futurecallstotheexecute()methodmaystillberejected,eventhoughcancelled
objectsareonthethreadpool'squeue:theexecute()methoddoesnotcheckthequeueforcancelledobjects.
Onewaytodealwiththissituationistocallthepurge()methodonthethreadpool.Thepurge()methodlooksovertheentirequeueandremovesany
cancelledobjects.Onecaveatapplies:ifasecondthreadattemptstoaddsomethingtothepool(usingtheexecute()method)atthesametimethefirst
threadisattemptingtopurgethequeue,theattempttopurgethequeuefailsandthecanceledobjectsremaininthequeue.
Abetterwaytocancelobjectswiththreadpoolsistousetheremove()methodofthethreadpool,whichimmediatelyremovesthetaskfromthethreadpool
queue.Theremove()methodcanbeusedwithstandardrunnableobjects.
publicclassFutureTask<V>implementsFuture<V>,Runnable{}
Thisclassisusedinternallybytheexecutorservice:theobjectreturnedfromthesubmit()methodofanexecutorserviceisaninstanceofthisclass.
However,youcanusethisclassdirectlyinprogramsaswell.Thismakessensewhenyouneedtomonitorthestatusofarunnableobjectwithinanexecutor:
youcanconstructafuturetaskwithanembeddedrunnableobjectandsendthefuturetasktotheexecute()methodofanexecutor(oranexecutorservice).
YoucanthenusethemethodsoftheFutureinterfacetomonitorthestatusoftherun()methodoftheembeddedrunnableobject.
AFutureTaskobjectcanholdeitheranembeddedrunnableorcallableobject,dependingonwhichconstructorisusedtoinstantiatetheobject:
publicFutureTask(Callable<V>task);
publicFutureTask(Runnabletask,Vresult);
Theget()methodofafuturetaskthatembedsacallabletaskreturnswhateverisreturnedbythecall()methodofthatembeddedobject.Theget()
methodofafuturetaskthatembedsarunnableobjectreturnswhateverobjectwasusedtoconstructthefuturetaskobjectitself.
WeusethisclassinournextexampleandalsoinourexamplesinChapter15.
Single-Threaded Access
InChapter7,wesawthethreadingrestrictionsplacedondevelopersusingtheSwinglibrary.Swingclassesarenotthreadsafe,sotheymustalwaysbecalled
fromasinglethread.InthecaseofSwing,thatmeansthattheymustbecalledfromtheeventdispatchingthread,usingtheinvokeLater()and
invokeAndWait()methodsoftheSwingUtilitiesclass.
Whatifyouhaveadifferentlibrarythatisn'tthreadsafeandwanttousethelibraryinyourmultithreadedprograms?Aslongasyouaccessthatlibraryfroma
singlethread,yourprogramwon'trunintoanyproblemswithdatasynchronization.
Here'saclassyoucanusetoaccomplishthat:
packagejavathreads.examples.ch10;
importjava.util.concurrent.*;
importjava.io.*;
publicclassSingleThreadAccess{
privateThreadPoolExecutortpe;
publicSingleThreadAccess(){
tpe=newThreadPoolExecutor(
1,1,50000L,TimeUnit.SECONDS,
newLinkedBlockingQueue<Runnable>());
}
publicvoidinvokeLater(Runnabler){
tpe.execute(r);
}
publicvoidinvokeAndWait(Runnabler)
throwsInterruptedException,ExecutionException{
FutureTasktask=newFutureTask(r,null);
tpe.execute(task);
task.get();
}
publicvoidshutdown(){
tpe.shutdown();
}
}
ThemethodsofthisclassfunctionexactlyliketheircounterpartsintheSwingUtilitiesclass:theinvokeLater()methodrunsitstaskasynchronously
andtheinvokeAndWait()methodrunsitsynchronously.Becausethethreadpoolhasonlyasinglethread,alltaskspassedtothe
SingleThreadAccessobjectareexecutedbyasinglethread,regardlessofhowmanythreadsusetheaccessobject:thetasksrunbythe
SingleThreadAccessobjectcancallthreadunsafeclasses.
InChapter9,weshowtheeffectofrunningourFibonaccicalculationswhenthethreadsareserializedouronlineexamplesforthischaptershow(asexample
2)howtousetheSingleThreadAccessclasstoachievethatsamebehavior.
Summary
Inthischapter,webeganexplorationofexecutors:utilitiesthatprocessRunnableobjectswhilehidingthreadingdetailsfromthedeveloper.Executorsare
veryusefulbecausetheyallowprogramstobewrittenasaseriesoftasksprogrammerscanfocusonthelogicoftheirprogramwithoutgettingboggeddown
indetailsabouthowthreadsarecreatedorused.
ThethreadpoolexecutorisoneoftwokeyexecutorsinJava.Inadditiontotheprogrammingbenefitscommontoallexecutors,threadpoolscanalsobenefit
programsthathavelotsofsimultaneoustaskstoexecute.Usingathreadpoolthrottlesthenumberofthreads.ThisreducescompetitionfortheCPUand
allowsCPUintensiveprogramstocompleteindividualtasksmorequickly.
ThecombinationofindividualtasksandalackofCPUresourcesiskeytowhentouseathreadpool.Threadpoolsareoftenconsideredimportantbecause
reusingthreadsismoreefficientthancreatingthreads,butthatturnsouttobearedherring.Fromaperformanceperspective,you'llseeabenefitfromthread
poolsbecausewhenthereislesscompetitionfortheCPU(becauseoffewerthreads),theaveragetimetocompleteanindividualtaskislessthanotherwise.
ThekeytoeffectivelyusingJava'sthreadpoolimplementationistoselectanappropriatesizeandqueueingmodelforthepool.Selectingaqueuingmodelisa
factorofhowyouwanttohandlemanyrequests:anunboundedqueueallowstherequeststoaccumulatewhileothermodelspossiblyresultinrejectedtasks
thatmustbehandledbytheprogram.Alittlebitofworkisrequiredtogetthemostoutofathreadpool.Buttherewardsbothintermsofthesimplification
ofprogramlogicandintermsofpotentialthroughputmakethreadpoolsveryuseful.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Ant
target
FibonacciCalculatorwithThreadPool
javathreads.examples.ch10.example1.ThreadPoolTestnRequestsNumberToCalculate
ch10
ThreadPoolSize
ex1
FibonacciCalculatorusing
javathreads.examples.ch10.example2.SingleThreadTestnRequests
ch10
SingleThreadAccess
NumberToCalculate
ex2
ThepropertiesfortheAnttasksare:
<propertyname="nThreads"value="10"/>
<propertyname="FibCalcValue"value="20"/>
<propertyname="ThreadPoolSize"value="5"/>
Chapter11.Task Scheduling
Inthepreviouschapter,weexaminedaninterestingaspectofthreads.Beforeweusedathreadpool,wewereconcernedwithcreating,controlling,and
communicatingbetweenthreads.Withathreadpool,wewereconcernedwiththetaskthatwewantedtoexecute.Usinganexecutorallowedustofocuson
ourprogram'slogicinsteadofwritingalotofthreadrelatedcode.
Inthischapter,weexaminethisideainanothercontext.Taskschedulersgiveustheopportunitytoexecuteparticulartasksatafixedpointintimeinthefuture
(or,morecorrectly,afterafixedpointintimeinthefuture).Theyalsoallowustosetuprepeatedexecutionoftasks.Onceagain,theyfreeusfrommanyof
thelowleveldetailsofthreadprogramming:wecreateatask,handitofftoataskscheduler,anddon'tworryabouttherest.
Javaprovidesdifferentkindsoftaskschedulers.Timerclassesexecutetasks(perhapsrepeatedly)atapointinthefuture.Theseclassesprovideabasictask
schedulingfeature.J2SE5.0hasanew,moreflexibletaskschedulerthatcanbeusedtohandlemanytasksmoreeffectivelythanthetimerclasses.Inthis
chapter,we'lllookintoalloftheseclasses.
Thesleep()method
InourdiscussionoftheThreadclass,weexaminedtheconceptofathreadwaitingforaspecificperiodoftime.Thepurposewaseithertoallowother
threadstoaccomplishrelatedtasks,toallowexternaleventstohappenduringthesleepingperiod,ortorepeatataskperiodically.Thetasksthatarelisted
afterthesleep()methodareexecutedatalatertimeperiod.Ineffect,thesleep()methodcontrolswhenthosetasksareexecuted.
Thejoin()method
OurdiscussionofthismethodoftheThreadclassrepresentsthefirsttimethatweexaminedalternatetaskstobeexecutedatalatertime.Thegoalof
thismethodistowaitforaspecificeventathreadtermination.However,theexpectedthreadterminationeventmaynotarrive,atleastnotwithinthe
desiredtimeperiod,sothejoin()methodprovidesatimeout.Thisallowsthemethodtoreturneitherbytheterminationofthethreadorbythe
expirationofthetimeoutthusallowingtheprogramtoexecuteanalternatetaskataspecifictimeandinaparticularsituation.
Thewait()method
Thewait()methodoftheObjectclassallowsathreadtowaitforanyevent.Thismethodalsoprovidestheoptiontoreturnifaspecifictimeperiod
passes.Thisallowstheprogramtoexecuteataskatalatertimeiftheeventoccursortospecifytheexacttimetoexecuteanalternatetaskiftheeventdoes
notoccur.Thisfunctionalityisalsoemulatedwithconditionvariablesusingtheawait()method.
TheTimeUnitclass
Thisclassisusedtodefineatimeperiod,allowingmethodstospecifyatimeperiodinunitsotherthanmillisecondsornanoseconds.Thisclassisused
bymanyoftheclassesaddedinJ2SE5.0tospecifyatimeperiodforatimeout.Thisclassalsoprovidesconveniencemethodstosupportcertainperiodic
requestsspecifically,itprovidesalternateimplementationsofthesleep(),join(),andwait()methodsthatuseaTimeUnitobjectastheir
timeoutargument.
TheDelayQueueclass
OurdiscussionoftheDelayQueueclassinChapter8isthefirsttimeweencounteraclassthatallowsdatatobeprocessedataspecifictime.Whena
producerplacesdatainadelayqueue,itisnotreadablebyconsumersuntilafteraspecificperiodpasses.Ineffect,thetasktoprocessthedataistobe
executedatalatertimeatimeperiodthatisspecifiedbythedataitself.
Astheseexamplesshow,insomecases,aprogramneedstoexecutecodeonlyafteraspecificeventorafteraperiodoftime.Muchofthetime,the
functionalityisindirectinthatthetimeoutisnotexpectedtooccur.Javaalsosupportstimeoutfunctionsdirectlybyprovidingtoolsthatallowtheprogramto
executespecifictasksataspecifictime.
We'veusedthesemethodsinourexampleswhenaprogramneedstoexecutecodeonlyafteraspecificeventorafteraperiodoftime.Thetiminginthese
caseshasalwaysbeenprovidedasatimeoutvalue:afteracertainperiodoftime,thethreadwouldregaincontrolandbeabletoexecutetheappropriatetask.
However,inthiscasecontrolalwaysresideswiththethread:executionoftheappropriatetaskissynchronouswithrespecttothecodebeingexecuted.Java
alsosupportsasynchronoustaskexecutioninalternatethreadsit'sthattypeofexecutionthatwe'llexamineintheremainderofthischapter.
TaskstobeexecutedbytheTimerclassmustinheritfromtheTimerTaskclass.AsintheThreadclass,thetasktobeexecutedistherun()method.In
fact,theTimerTaskclassactuallyimplementstheRunnableinterface.TheTimerclassrequiresaTimerTaskobjectsothattwomethodscanbeattached
tothetaskthesemethodscanbeusedtomaintainthetask.ThesemethodsdonothavetobeimplementedtheTimerTaskclassprovidesadefault
implementationforthem.AclassthatinheritsfromtheTimerTaskclassneedonlyimplementtherun()method.
Thedownsideofthistechniqueisthatthetaskcan'tinheritfromotherclasses.SincetheTimerTaskclassisnotaninterface,itmeansthattaskshaveto
eitherbecreatedfromclassesthatdon'talreadyinheritfromotherclasses,orwrapperclasseshavetobecreatedtoforwardtherequest.
Thecancel()methodisusedtostoptheclassfrombeingexecuted.Ataskthatisalreadyexecutingisunaffectedwhenthismethodiscalled.However,if
thetaskisrepeating,callingthecancel()methodpreventsfurtherexecutionoftheclass.Fortasksthatareexecutedonlyonce,thecancel()method
returnswhetherthetaskhasbeencancelled:ifthetaskiscurrentlyrunning,hasalreadyrun,orhasbeenpreviouslycancelled,itreturnsabooleanvalueof
false.Forrepeatingtasks,thismethodalwaysreturnsabooleanvalueoftrue.
ThescheduledExecutionTime()methodisusedtoreturnthetimeatwhichthepreviousinvocationofarepeatingtaskoccurred.Ifthetaskiscurrently
running,itisthetimeatwhichthetaskbegan.Ifthetaskisnotrunning,itisthetimeatwhichthepreviousexecutionofthetaskbegan.Itspurposeisabit
obscurebutitwillmakemoresenseafterwediscusstheTimerclass.
HereistheinterfaceoftheTimerclass:
publicclassTimer{
publicTimer();
publicTimer(booleanisDaemon);
publicTimer(Stringname);
publicTimer(Stringname,booleanisDaemon);
publicvoidschedule(TimerTasktask,longdelay);
publicvoidschedule(TimerTasktask,Datetime);
publicvoidschedule(TimerTasktask,longdelay,longperiod);
publicvoidschedule(TimerTasktask,DatefirstTime,longperiod);
publicvoidscheduleAtFixedRate(TimerTasktask,longdelay,longperiod);
publicvoidscheduleAtFixedRate(TimerTasktask,DatefirstTime,longperiod);
publicvoidcancel();
publicintpurge();
}
TheTimerclassprovidesthemeanstoexecutetasksatalatertime.Thetasksthatarescheduledareplacedinanorderedqueueandareexecutedsequentially
byasinglethread.
FourconstructorsareprovidedtocreatedifferentversionsoftheTimerclass.Themostimportantparameteroftheseconstructorsallowsthedefinitionof
whetherthecreatedthreadisadaemonthread(seeChapter13).Thisisusefulfortaskswhichareneededonlyiftheuserisstillinteractingwiththeprogram.
Ifthetimerthreadisadaemonthread,theprogramcanexitwhenalltheuserthreadsterminate.Theotherparameterisusedtonamethethreadthisis
importantifthethreadsaretobemonitoredbyadebugger.
Thefirsttwooverloadedversionsoftheschedule()methodareusedtoscheduleonetimetasks.Thefirstallowsforthespecificationofadelay:atime
periodinmillisecondsrelativetothecurrenttime.Thesecondallowsforthespecificationofanabsolutetime.
Thelasttwooverloadedversionsoftheschedule()methodareusedtoschedulerepeatingtasks.Thethirdparameterisusedtospecifytheperiodin
millisecondsbetweeninvocationsoftherepeatedtasks.
Thereareafewimportantissuesinthetimerimplementation,particularlyforrepeatedtasks.First,onlyasinglethreadexecutesthetasks.Whileitis
recommendedthatthetasksexecutedbytheTimerclassbeshortlived,nocheckensuresthatthisisso.ThismeansthatiftheTimerobjectis
overwhelmed,ataskmaybeexecutedatatimemuchlaterthanthespecifiedtime.Forrepeatedtasks,theschedule()methoddoesnottakethisinto
account.Thescheduletimeisallowedtodrift,meaningthatthenextiterationofthetaskisbasedonthepreviousiteration.Thisisnotveryusefulifthetaskis
usedtomaintainaclockorothertimecriticaltask.
Twomechanismscanbeusedtoresolvethis.ThefirstmechanismisthetwooverloadedscheduleAtFixedRate()methods.Theschedule()method
schedulesthenextexecutionofthetaskbasedonwhenthepreviousexecutionactuallyoccurred.Thenextiterationofthetaskscheduledbythe
scheduleAtFixedRate()methodiscalculatedfromwhenthepreviousiterationwassupposedtoexecutenotwhenthepreviousiterationactually
executes.
ThesecondmechanismisthescheduledExecutionTime()methodoftheTimerTaskclass.Thismethodcanbeusedbythetaskitselftodetermine
whenthetaskissupposedtorun.Basedonthecomparisontothecurrenttime,thetaskcanadjustitsbehavior.Thisisevenmoreimportantwhenthe
scheduleAtFixedRate()methodisusedtoschedulethetask.Sincethetasksarenotallowedtodrift,morethanoneiterationoftherepeatedtaskmaybe
waitingtoexecute.Asaresult,atimertaskmaywanttoskipaparticularexecutionifitknowsthatanotherexecutionispendinginthequeue.Forexample,a
taskthatrunseveryfivesecondscantellifithasmissedanexecutionbyusingthiscode:
publicclassMyTimerTaskextendsTimerTask{
publicvoidrun(){
if(System.currentTimeMillis()scheduledExecutionTime()>5000){
//We'remorethanfivesecondsoff;skipthisbecauseanothertask
//willalreadyhavebeenscheduled.
return;
}
...
}
}
Table111showswhentaskswouldbeexecutedunderdifferentschedulingmodelsoftheTimerclass.Inthisexample,we'reassumingthatthetaskistobe
runeverysecond,executesfor.1seconds,andthesystembecomesboggeddownfor.5secondsbetweenthesecondandthirditeration.Theschedule()
methoddriftsby.5secondsonsubsequentexecutions.ThescheduleAtFixedRate()methodrunsthedelayediteration.5secondslatebutstillexecutes
theremainingiterationsaccordingtotheoriginalschedule.Neithertakesintoaccountthetimerequiredtoexecutethetask.
Table111.Executiontimeofjava.util.Timertasks
Executionstarttime
Method
Iteration1
Iteration2
Iteration3
Iteration4
Iteration5
schedule()
1seconds
2seconds
3.5seconds
4.5seconds
5.5seconds
scheduleAtFixedRate()
1seconds
2seconds
3.5seconds
4seconds
5seconds
Thecancel()methodisprovidedbytheTimerclasstodestroythetimer.Allthetasksinthetimeraresimplycancelled,andnonewtasksareallowedto
bescheduled.TheTimerobjectcannolongerbeusedtoscheduleanymoretasks.Ifataskiscurrentlyexecuting,itisallowedtofinishcurrentlyexecuting
tasksarenotinterrupted.
Thepurge()methodisusedformaintenance.Thetask'scancel()methoddoesnotactuallydeletethetaskfromthetaskqueuethetaskissimply
markedascancelled.Thetaskisdeletedfromthequeuebythetimerwhenitistimeforthetasktoexecute:becausethetaskismarkedascancelled,thetaskis
skippedanddeletedfromthequeueatthattime.Thepurge()methodisimportantonlywhenalargenumberoftasksarebeingcancelled(orthetasks
themselvesconsumealotofmemory).Bypurgingthetimer,thetaskobjectsareremovedfromthequeue,allowingthemtobegarbagecollected.
importjava.util.*;
importjava.net.*;
publicclassURLPingTaskextendsTimerTask{
publicinterfaceURLUpdate{
publicvoidisAlive(booleanb);
}
URLurl;
URLUpdateupdater;
publicURLPingTask(URLurl){
this(url,null);
}
publicURLPingTask(URLurl,URLUpdateuu){
this.url=url;
updater=uu;
}
publicvoidrun(){
if(System.currentTimeMillis()scheduledExecutionTime()>5000){
//Letthenexttaskdoit
return;
}
try{
HttpURLConnectionhuc=(HttpURLConnection)url.openConnection();
huc.setConnectTimeout(1000);
huc.setReadTimeout(1000);
intcode=huc.getResponseCode();
if(updater!=null)
updater.isAlive(true);
}catch(Exceptione){
if(updater!=null)
updater.isAlive(false);
}
}
}
Therun()methodperiodicallycontactsthegivenURLandthenupdatesthestatuswatcherdependingonwhetherornotreadingtheURLwassuccessful.
Notethatifmorethanfivesecondshaveelapsedsincethelasttimethetaskruns,thetaskskipsitself.
Theprogramthatsetsupthetasklookslikethis:
packagejavathreads.examples.ch11.example1;
importjava.awt.*;
importjava.awt.event.*;
importjava.net.*;
importjavax.swing.*;
importjava.util.Timer;
publicclassURLMonitorPanelextendsJPanelimplementsURLPingTask.URLUpdate{
Timertimer;
URLurl;
URLPingTasktask;
JPanelstatus;
JButtonstartButton,stopButton;
publicURLMonitorPanel(Stringurl,Timert)throwsMalformedURLException{
setLayout(newBorderLayout());
timer=t;
this.url=newURL(url);
add(newJLabel(url),BorderLayout.CENTER);
JPaneltemp=newJPanel();
status=newJPanel();
status.setSize(20,20);
temp.add(status);
startButton=newJButton("Start");
startButton.setEnabled(false);
startButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventae){
makeTask();
startButton.setEnabled(false);
stopButton.setEnabled(true);
}
});
stopButton=newJButton("Stop");
stopButton.setEnabled(true);
stopButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventae){
task.cancel();
startButton.setEnabled(true);
stopButton.setEnabled(false);
}
});
temp.add(startButton);
temp.add(stopButton);
add(temp,BorderLayout.EAST);
makeTask();
}
privatevoidmakeTask(){
task=newURLPingTask(url,this);
timer.schedule(task,0L,5000L);
}
publicvoidisAlive(finalbooleanb){
SwingUtilities.invokeLater(newRunnable(){
publicvoidrun(){
status.setBackground(b?Color.GREEN:Color.RED);
status.repaint();
}
});
}
publicstaticvoidmain(String[]args)throwsException{
JFrameframe=newJFrame("URLMonitor");
Containerc=frame.getContentPane();
c.setLayout(newBoxLayout(c,BoxLayout.Y_AXIS));
Timert=newTimer();
for(inti=0;i<args.length;i++){
c.add(newURLMonitorPanel(args[i],t));
}
frame.addWindowListener(newWindowAdapter(){
publicvoidwindowClosing(WindowEventevt){
System.exit(0);
}
});
frame.pack();
frame.show();
}
}
EachindividualpanelmonitorsasingleURL.NotethattheisAlive()methodrunsfromthetimerthread,soitsinvocationofSwingmethodsisplaced
withinacalltotheinvokeLater()method.Alsonotethatsinceataskcannotbereused,theactionPerformed()methodassociatedwiththeStart
buttonmustsetupanewtask.
ThisapplicationpointsoutthebasicshortcomingsoftheTimerclass.We'vesetitupsothatallthepanelsshareasingleinstanceofthetimer,whichmeansa
singlethread.Althoughourtaskusestimeoutstotalktothewebserver,it'sconceivablethatasingleexecutionoftherun()methodofthetaskcouldtake
almosttwoseconds(thoughit'smorelikelytotakeonlyonesecondifthesiteisdown).Ifyoumonitor10sitesandyourISPgoesdown,thesingletimer
threadendsupwithabacklogoftasks.That'sthereasonweputlogicintotherun()methodofthetasktochecktoseewhetheritmisseditsscheduled
executiontime.
Thealternativeistocreateanewtimerforeachpanel.Inthatcase,wedon'thavetoworryaboutabacklogoftasks.Thedownsideisthatwenowhaveone
threadforeverysitewe'remonitoring.That'snotabigdealunlesswe'remonitoringthousandsofsites,butit'snotoptimaleither.We'llrevisitthislaterinthis
chapter.
publicclassTimer{
publicTimer(intdelay,ActionListenerlistener);
publicvoidaddActionListener(ActionListenerlistener);
publicvoidremoveActionListener(ActionListenerlistener);
publicActionListener[]getActionListeners();
publicEventListener[]getListeners(ClasslistenerType);
publicstaticvoidsetLogTimers(booleanflag);
publicstaticbooleangetLogTimers();
publicvoidsetDelay(intdelay);
publicintgetDelay()
publicvoidsetInitialDelay(intinitialDelay);
publicintgetInitialDelay();
publicvoidsetRepeats(booleanflag);
publicbooleanisRepeats();
publicvoidsetCoalesce(booleanflag);
publicbooleanisCoalesce();
publicvoidstart();
publicbooleanisRunning();
publicvoidstop();
publicvoidrestart();
}
Thisclassisnotreallyagenericscheduler.Infact,eventhoughmultiplecallbacks(eventlisteners)canbeattachedtothetimer,ithasonlyoneschedule:all
thelistenersusethescheduledefinedbytheTimerclassitself(ratherthanthescheduledefinedbyparticulartasks).Tasksthatrequireadifferentschedule
needadifferentinstanceoftheSwingtimer.Mostofthemethodsprovidedbythisclassareusedtoconfigurethescheduleandcontrolthetimer.
Unlikethejava.util.Timerclass,thisTimerclassusestheActionListenerinterface.ThisprovidesaninterfacethatSwingdevelopersare
accustomedto:allSwingobjectsuseeventlistenerstoexecutecallbacks.Whenascheduledtimeisreached,itistreatedasanyotherevent(suchasabutton
press):theregisteredactionlistenersarecalled.
Theconstructortotheclasstakestwoparameters.Thefirstisthedelayinmilliseconds.Thisvalueisusedbythetimerasboththeinitialtimetowaittofire
thefirstactionlistenerandthetimetowaitbetweenrepeatedfiringsoftheactionlisteners.Thesecondparameterisanactionlistenertofire.Bothofthese
parameterscanbemodifiedatalatertime.
TheaddActionListener()andremoveActionListener()methodsareusedtoaddlistenerstoandremovelistenersfromthetimer.The
getActionListeners()methodisusedtoretrievethelistenersthathavebeenregisteredtothetimer.ThegetListeners()methodprovidesthe
addedqualificationoftheeventlistenertype.Thisallowsthedevelopertogetspecifictypesoflistenersthatareregisteredtothetimer.Inmostcases,thisis
probablynotveryuseful,asthelimitationofthetimerasagenericscheduleralsolimitsthenumberofactionlistenersregisteredtoeachtimer.
ThegetDelay()andsetDelay()methodsareusedtoretrieveandmodifythetimebetweenrepeatedevents(whichbydefaultissetintheconstructor).
Thisallowsittobedifferentfromtheinitialdelaytime.ThatdelaytimeishandledbythegetInitialDelay()andsetInitialDelay()methods.
TheisRepeats()andsetRepeats()methodsareusedtocontrolwhethereventsarerepeated.Bydefault,thetimerrepeatsevents,asthisTimerclass
wasoriginallydesignedfortaskssuchasablinkingcursor.TheisCoalesce()andsetCoalesce()methodsareusedtohandlerepeatedmethodsthat
arebacklogged.Forexample,ifamethodistobecalledonceeverysecond,andthreesecondshaveelapsed,thenthelistenermayhavetobecalledthree
times.Ifthecoalesceflagisset,thelisteneriscalledonlyonce.Thisisimportantfortaskssuchasblinkingthecursor.Ifthetimerhasalreadymissedtwo
blinks,blinkingthreetimesveryfastdoesnotfixtheproblemitisbettertojustskipthemissedblinks.
ThegetLogTimers()andsetLogTimers()methodsareusedtocontroldebuggingofthetimer.Ifdebuggingisactivated,messagesaresenttostandard
outputtoreporttheactionsofthetimer.
Finally,thetimermustbeactivateduponcompletionoftheregistrationofthelisteners(and,possibly,adjustingtheinitialdelayandrepeattimes).Thisis
accomplishedbythestart()method.Thetimercanlaterbeterminatedbycallingthestop()method.Therestart()methodresetsthetimer:the
timerthenwaitsuntilitsinitialdelaytimeperiodhaselapsed,atwhichpointitstartscallingitslisteners.TheisRunning()methodisusedtodetermine
whetherthetimerhasbeenstarted.
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
importjava.util.concurrent.*;
importjava.util.concurrent.locks.*;
importjavathreads.examples.ch11.*;
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsActionListener,CharacterListener{
privateintcurX;
privateTimertimer;
publicAnimatedCharacterDisplayCanvas(CharacterSourcecs){
super(cs);
timer=newTimer(100,this);
}
publicsynchronizedvoidnewCharacter(CharacterEventce){
curX=0;
tmpChar[0]=(char)ce.character;
repaint();
}
publicsynchronizedvoidpaintComponent(Graphicsgc){
if(tmpChar[0]==0)
return;
Dimensiond=getSize();
intcharWidth=fm.charWidth(tmpChar[0]);
gc.clearRect(0,0,d.width,d.height);
gc.drawChars(tmpChar,0,1,curX++,fontHeight);
if(curX>d.widthcharWidth)
curX=0;
}
publicvoidactionPerformed(ActionEventae){
repaint();
}
publicvoidsetDone(booleanb){
if(!b)
timer.start();
elsetimer.stop();
}
}
Notethatthisimplementationismuchsimplerthanourpreviousimplementations.Previously,wesetupathreadinthesetDone()methodnow,we
simplycallthetimerstart()method.
Usingthetimerhasalsoallowedustosimplifythelockingaroundthecallstotherepaint()method.Knowingwhentheanimationshouldrunusedto
requireawaitandnotifymechanism(orconditionvariable).Nowwejustdeferthattothetimer.TheTimerclassitselfhasthewaitinglogicwithinit:
operationally,wehaven'tsavedanything.Butintermsofdevelopment,usingatimerhassavedussomeeffort.Thisisaclearexampleofwhyusinghigher
levelthreadconstructsmakesthingssimplerforthedeveloper.
Second,theTimerTaskclassisnotnecessary.Itisusedtoattachmethodstothetaskitself,providingtheabilitytocancelthetaskandtodeterminethelast
scheduledtime.Thisisnotnecessary:itispossibleforthetimeritselftomaintainthisinformation.Italsorestrictswhatcanbeconsideredatask.Classes
usedwiththeTimerclassmustextendtheTimerTaskclassthisisnotpossibleiftheclassalreadyinheritsfromanotherclass.Itismuchmoreflexibleto
allowanyRunnableobjecttobeusedasthetasktobeexecuted.
Finally,relyingupontherun()methodistoorestrictivefortasks.Whileitispossibletopassparameterstothetaskbyusingparametersintheconstructor
ofthetaskthereisnowaytogetanyresultsorexceptions.Therun()methodhasnoreturnvariable,norcanitthrowanytypeofexceptionsotherthan
runtimeexceptions(andevenifitcould,thetimerthreadwouldn'tknowhowtodealwithit).
TheScheduledThreadPoolExecutorclasssolvesallthreeoftheseproblems.Itusesathreadpool(actually,itinheritsfromthethreadpoolclass)and
allowsthedevelopertospecifythesizeofthepool.ItstorestasksasRunnableobjects,allowinganytaskthatcanbeusedbythethreadobjecttobeusedby
theexecutor.BecauseitcanworkwithobjectsthatimplementtheCallableinterface,iteliminatestherestrictivebehaviorofrelyingsolelyonthe
Runnableinterface.
Here'stheinterfaceoftheScheduledThreadPoolExecutorclassitself:
publicclassScheduledThreadPoolExecutorextendsThreadPoolExecutor{
publicScheduledThreadPoolExecutor(intcorePoolSize);
publicScheduledThreadPoolExecutor(intcorePoolSize,
ThreadFactorythreadFactory);
publicScheduledThreadPoolExecutor(intcorePoolSize,
RejectedExecutionHandlerhandler);
publicScheduledThreadPoolExecutor(intcorePoolSize,
ThreadFactorythreadFactory,
RejectedExecutionHandlerhandler);
public<V>ScheduledFuture<V>schedule(Callable<V>callable,
longdelay,TimeUnitunit);
publicScheduledFuture<V>scheduleAtFixedRate(Runnablecommand,
longinitialDelay,longperiod,TimeUnitunit);
publicScheduledFuture<V>scheduleWithFixedDelay(
Runnablecommand,longinitialDelay,
longdelay,TimeUnitunit);
publicvoidexecute(Runnablecommand);
publicvoidshutdown();
publicListshutdownNow();
publicvoidsetContinueExistingPeriodicTasksAfterShutdownPolicy(
booleanvalue);
publicbooleangetContinueExistingPeriodicTasksAfterShutdownPolicy();
publicvoidsetExecuteExistingDelayedTasksAfterShutdownPolicy(
booleanvalue);
publicbooleangetExecuteExistingDelayedTasksAfterShutdownPolicy();
}
TheScheduledThreadPoolExecutorclassprovidesfourconstructorstocreateanobject.Theseparametersarebasicallythesameparametersasthe
threadpoolconstructorssincethisexecutorinheritsfromthethreadpoolexecutor.Therefore,thisclassisalsoathreadpool,meaningthatsomeofthe
parametersassignedbytheseconstructorscanalsoberetrievedandmodifiedbythemethodsoftheThreadPoolExecutorclass.
Note,however,thattheconstructorshavenoparametertospecifythemaximumnumberofthreadsorthetypeofqueuethethreadpoolshoulduse.The
scheduledexecutoralwaysusesanunboundedqueueforitstasks,andthesizeofitsthreadpoolisalwaysfixedtothenumberofcorethreads.Thenumberof
corethreads,however,canstillbemodifiedbycallingthesetCorePoolSize()method.
Theschedule()methodisusedtoscheduleaonetimetask.YoucanusetheScheduledFutureobjectreturnedbythismethodtoperformtheusual
tasksonthecallableobject:youcanretrieveitsresult(usingtheget()method),cancelit(usingthecancel()method),orseeifithascompleted
execution(usingtheisDone()method).
ThescheduleAtFixedRate()methodisusedtoschedulearepeatedtaskthatisnotallowedtodrift.Thisisbasicallythesameschedulingmodelasthe
scheduleAtFixedRate()methodoftheTimerclass.
ThescheduleWithFixedDelay()methodisusedtoschedulearepeatedtaskwheretheperiodbetweenthetasksremainsconstantthisisusefulwhen
thedelaybetweeniterationsistobefixed.Forinstance,thismodelisbetterforanimationsincethereisnoreasontohaveanimationcyclesaccumulateifthe
starttimesdrift.Ifonecycleoftheanimationrunslate,thereisnoadvantagetorunningthenextcycleearlier.
Table112showswhentaskswouldbeexecutedunderdifferentschedulingmodelsoftheScheduledThreadPoolExecutorclass.Inthisexample,
we'reagainassumingthatthetaskistoberuneverysecond,executesfor.1seconds,andthesystembecomesboggeddownfor.5secondsbetweenthe
secondandthirditeration.ThescheduleAtFixedRate()methodrunsthedelayediteration.5secondslatebutstillexecutestheremainingiterations
accordingtotheoriginalschedule(exactlythesameasthejava.util.Timerclass).ThescheduleWithFixedDelay()methodtakesintoaccountthe
executiontimeofthetaskthisiswhyeachiterationdriftsby.1seconds.Itdoesnotcompensateforthe.5seconddelay,soitdriftsovertime.
Table112.Executiontimeofjava.util.Timertasks
Executionstarttime
Method
Iteration1
Iteration2
Iteration3
Iteration4
Iteration5
scheduleAtFixedRate()
1seconds
2seconds
3.5seconds
4seconds
5seconds
scheduleWithFixedDelay()
1seconds
2.1seconds
3.7seconds
4.8seconds
5.9seconds
Theexecute()andsubmit()methodsareusedtoscheduleatasktorunimmediately.ThesemethodsarepresentmainlybecausetheExecutor
interfacerequiresthem.Still,itmaybeusefulforonetasktoaddothertaskstoberuninthepoolratherthanexecutethemdirectly,becausethentheprimary
taskdoesn'townthethreadinthepoolforahugeperiodoftime.Italsoallowsthethreadpooltoassignthesubtaskstootherthreadsinthepoolifthepoolis
notbusy.
Theshutdown()andshutdownNow()methodsarealsopartofthethreadpoolclass.Theshutdown()methodisusedtoshutdowntheexecutorbut
allowsallpendingtaskstocomplete.TheshutdownNow()methodisusedtotrytocancelthetasksinthepoolinadditiontoshuttingdownthethreadpool.
However,thisworksdifferentlyfromathreadpoolbecauseofrepeatingtasks.Sincecertaintasksrepeat,taskscouldtechnicallyrunforeverduringagraceful
shutdown.
Tosolvethis,thetaskexecutorprovidestwopolicies.TheExecuteExistingDelayedTasksAfterShutdownPolicyisusedtodeterminewhetherthe
tasksinthequeueshouldbecancelledupongracefulshutdown.TheContinueExistingPeriodicTasksAfterShutdownPolicyisusedto
determinewhethertherepeatingtasksinthequeueshouldbecancelledupongracefulshutdown.Therefore,settingbothtofalseemptiesthequeuebut
allowscurrentlyrunningtaskstocomplete.ThisissimilartohowtheTimerclassisshutdown.TheshutdownNow()methodcancelsallthetasksand
alsointerruptsanytaskthatisalreadyexecuting.
Withthesupportofthreadpools,callabletasks,andfixeddelaysupport,youmightconcludethattheTimerclassisobsolete.However,theTimerclasshas
someadvantages.First,itprovidestheoptiontospecifyanabsolutetime.Second,theTimerclassissimplertouse:itmaybepreferableifonlyafewtasks
orrepeatedtasksareneeded.
OurSwingcomponenthasjustafewchanges:
packagejavathreads.examples.ch11.example3;
...
importjava.util.concurrent.*;
publicclassURLMonitorPanelextendsJPanelimplementsURLPingTask.URLUpdate{
ScheduledThreadPoolExecutorexecutor;
ScheduledFuturefuture;
...
publicURLMonitorPanel(Stringurl,ScheduledThreadPoolExecutorse)
throwsMalformedURLException{
executor=se;
...
stopButton.addActionListener(newActionListener(){
publicvoidactionPerformed(ActionEventae){
future.cancel(true);
startButton.setEnabled(true);
stopButton.setEnabled(false);
}
});
...
}
privatevoidmakeTask(){
task=newURLPingTask(url,this);
future=executor.scheduleAtFixedRate(
task,0L,5L,TimeUnit.SECONDS);
}
publicstaticvoidmain(String[]args)throwsException{
...
ScheduledThreadPoolExecutorse=newScheduledThreadPoolExecutor(
(args.length+1)/2);
for(inti=0;i<args.length;i++){
c.add(newURLMonitorPanel(args[0],se));
}
...
}
}
Themainenhancementthatthischangehasboughtusistheabilitytospecifyanumberofthreadsfortheexecutor.We'vechosenhalfasmanythreadsasthe
machineswe'remonitoring:inbetweenthenumberofsuboptimalchoiceswehadpreviously.Inthiscase,itwouldhavebeenevenmoreidealforthetask
executortobemoreflexibleinitsthreaduse.
classTimeoutTaskimplementsCallable{
publicIntegercall()throwsIOException{
returnnewInteger(0);
}
}
Asrequired,we'veimplementedtheCallableinterface.Inthissimpleexample,wedon'tactuallycareaboutthereturnvalue:ifthetaskhasrun,thelicense
hasexpired.Inamorecomplicatedcase,thelicensetaskmightcheckwithalicenseserverandreturnamoreinterestingresult.Checkingwiththelicense
servermightcreateanIOException,whichiswhywe'vedeclaredthatthistaskthrowsthatexception.
Nowwemustaddthistoourmonitor:
packagejavathreads.examples.ch11.example4;
publicclassURLMonitorPanelextendsJPanelimplementsURLPingTask.URLUpdate{
staticFuture<Integer>futureTaskResult;
staticvolatilebooleandone=false;
...
privatevoidcheckLicense(){
if(done)return;
try{
IntegerI=futureTaskResult.get(0L,TimeUnit.MILLISECONDS);
//Ifwegotaresult,weknowthatthelicensehasexpired
JOptionPane.showMessageDialog(null,
"Evaluationtimeperiodhasexpired","Expired",
JOptionPane.INFORMATION_MESSAGE);
done=true;
}catch(TimeoutExceptionte){
//Taskhasn'trun;justconinue
}catch(InterruptedExceptionie){
//Taskwasexternallyinterrupted
}catch(ExecutionExceptionee){
//TaskthrewIOException,whichcanbeobtainedlike
IOExceptionioe=(IOException)ee.getCause();
//Cleanupaftertheexception
}
}
publicvoidisAlive(finalbooleanb){
try{
SwingUtilities.invokeAndWait(newRunnable(){
publicvoidrun(){
checkLicense();
if(done){
future.cancel(true);
startButton.setEnabled(false);
stopButton.setEnabled(false);
return;
}
status.setBackground(b?Color.GREEN:Color.RED);
status.repaint();
}
});
}catch(Exceptione){}
}
publicstaticvoidmain(String[]args)throwsException{
...
TimeoutTasktt=newTimeoutTask();
futureTaskResult=se.schedule(tt,120,TimeUnit.SECONDS);
...
}
}
ThecheckLicense()methodiscalledeverytimestatusisreporteditpollsthetimeouttask.Whenthepollsucceeds,thecheckLicense()methodsets
adoneflagsothatotherpanelsknowthatthelicensehasexpired(thedoneflagisstaticandsharedamongallpanels).Alternately,wecouldleteachpanel
pollthefutureTaskResultobjectitself.
Ifyoulookcarefully,you'llnoticethatthere'snosynchronizationforthecheckLicense()methodandthatitappearsthattheoptionpanemightget
displayedtwiceiftwopanelsinvokethatmethodatthesametime.However,that'snotpossiblebecausethecheckLicense()methodiscalledviathe
invokeAndWait()method.Thatblockstheeventdispatchingthreadsowearealreadyassuredthatonlyonethreadatatimeisexecutingthe
checkLicense()method.
Summary
Inthischapter,we'velookedatvariouswaysinwhichtasksmaybescheduledinthefuture.Thesimplestwaytodothisistousethejava.util.Timer
class,whichcanruninstancesofaspecialclass(theTimerTaskclass)atapointinthefuture,repeatingthetaskifnecessary.Eachinstanceofatimerisa
singlethreadthatthreadcanhandlemultipletasksbutlongrunningtasksmayneedtheirownthread(andconsequentlytheirowntimer).
Thejavax.swing.Timerclassisfunctionallysimilar,exceptthatitensuresthattasksarerunontheeventdispatchingthreadsothattheymaysafely
accessSwingcomponents.However,thejavax.swing.Timerclasshasafixedtimescheduleforallthetasksitrunstasksthathavedifferentscheduling
needsrequiredifferentinstancesofthetimer.
Finally,theScheduledThreadPoolExecutorclassprovidesamoreflexible(butmorecomplex)interfacetotaskscheduling.Becauseitusesathread
pool,itcanbemoreefficientwhenrunningalotoftaskssimultaneously.ItalsoallowsyoutopollfortaskstatusortousegenericRunnableobjectsasyour
task.
Thekeybenefitoftaskexecutorsandtimersisthattheyfreeyoufromhavingtoworryaboutthreadrelatedprogrammingforyourtasks:yousimplyfeedthe
tasktothetimerorexecutorandletitworryaboutthenecessarythreadcontrols.Thismakesthecodethatyouwritethatmuchsimpler.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
URLMonitorwithjava.util.Timerclass
javathreads.examples.ch11.example1.URLMonitorURL1URL2...
ch11ex1
TypeTesterwithTimeranimation
javathreads.examples.ch11.example2.SwingTypeTester
ch11ex2
URLMonitorwithscheduledexecutor
javathreads.examples.ch11.example3.URLMonitorURL1URL2...
ch11ex3
URLMonitorwithtimeout
javathreads.examples.ch11.example4.URLMonitorURL1URL2...
ch11ex4
TheantpropertytospecifytheURLis:
<propertyname="hostlist"value="http://www.ora.com/"/>
Unfortunately,Antoffersnowaytospecifymultiplehostnames.IfyouwanttotryaURLmonitorwithmorethanoneURL,youmustexecutetheclass
directly.
Figure121.Networkconnectionsbetweenclientsandaserver
Onceadataconnectionhasbeennegotiated,theserverandclientcommunicatethroughtheprivateconnection.Ingeneral,thisprocessisgeneric:most
developersareconcernedwiththedatasockets(theprivateconnection).Furthermore,thedatasocketsontheserversideareusuallyselfcontainedtoa
particularclient.Whileit'spossibletohavedifferentmechanismsthatdealwithmanydatasocketsatthesametime,generallythesamecodeisusedtodeal
witheachofthedatasocketsindependently.
Sincethesetupisgeneric,wecandevelopagenericTCPServerclassthathandlesthesetupanddefersthedataprocessingtoitssubclasses.This
TCPServerclasscreatestheserversocketandacceptsconnections.Foreachconnection,itspawnsanewthread(acloneofitself,sothatthenewthreadhas
acopyofalltheinterestingdatathattheserverholds).Here'stheimplementationofthisclass,whichservesasthesuperclassformanyoftheexamplesinthis
chapter:
packagejavathreads.examples.ch12;
importjava.net.*;
importjava.io.*;
publicclassTCPServerimplementsCloneable,Runnable{
Threadrunner=null;
ServerSocketserver=null;
Socketdata=null;
privatebooleandone=false;
publicsynchronizedvoidstartServer(intport)throwsIOException{
if(runner==null){
server=newServerSocket(port);
runner=newThread(this);
runner.start();
}
}
publicsynchronizedvoidstopServer(){
done=true;
runner.interrupt();
}
protectedsynchronizedbooleangetDone(){
returndone;
}
publicvoidrun(){
if(server!=null){
while(!getDone()){
try{
Socketdatasocket=server.accept();
TCPServernewSocket=(TCPServer)clone();
newSocket.server=null;
newSocket.data=datasocket;
newSocket.runner=
newThread(newSocket);
newSocket.runner.start();
}catch(Exceptione){}
}
}else{
run(data);
}
}
publicvoidrun(Socketdata){
}
}
TheTCPServerclassimplementstheRunnableinterfaceitcreatesmultiplethreadsandcopiesofitselftorunineachofthosethreads.Creatingthecopies
requiresthattheserverimplementtheCloneableinterfaceaswell.SincethefirstTCPServerobjectoperatesontheserversocket(whiletheclonesoperate
onthedatasockets),theTCPServerclassmustbewrittentoservicebothkindsofsockets.
Thelogictohandletheclientsiscontainedwithintherun()method.Theconditionalatthebeginningoftherun()methodiswhatdistinguishesbetween
thetypeofsockettobehandled.Whenwefirstentertherun()method,theservervariableissettotheserversocket,sowecontinueintotheinnerloop
thatacceptsnewconnections.Whenanewconnectionhasbeenaccepted,weclonetheTCPServerobjectandsettheservervariableintheclonedobjectto
null.Theclonedobjectisthenexecutedinanewthread.Whentheclonedobjectexecutestherun()method,itsservervariableisnullandsoitcallsthe
run(Socketdata)method.Inthebaseclass,thatmethoddoesnothingtohaveausefulTCPServer,youmustextendit(whichwe'lldonext).
Tostarttheserver,youmustcallthestartServer()method.Thatmethodcreatesathreadthatrunstheserver.Byhandlingtheserversocketinthis
thread,thestartServer()methodcanreturnimmediately,andthesameprogramcaninstantiatemultipleservers.ThestopServer()methodisusedto
stoptheserver:itfollowsourtraditionalpatternofsettingadoneflagandinterruptingthetargetthread(therunnerthread).NotethatthestopServer()
methodstopstheserverthread,whichpreventstheserverfromacceptingnewclientconnectionsbutallexistingclientconnectionsandthreadsremain
running.Thisallowsforagracefulshutdown.It'sasimpleextensiontotheclasstokeeptrackofallclientthreadsandinterruptthemifyouwantthemtoshut
downaswell.
Onemorepointaboutthisimplementation:you'llnoticethatthestartServer()andstopServer()methodsaresynchronizedbecausetheyoperateon
shareddata,butthatdataappearstobeaccessedfromtheunsynchronizedrun()method.Appearancesherearedeceiving.Ineveryclientthread,theclient
hasaseparatecloneoftheobject,soeachthreadisoperatingonitsownprivatedata.Asaresult,thatdataneednotbesynchronized.Iftheclientthreadsneed
tosharedata,theyareresponsibleformakingsurethatthedataisproperlysynchronized.
Forourfirstexample,we'llsubclasstheTCPServerclasstoperformI/Owithintherun(Socketdata)method.Foracompleteexample,wemust
providetheTCPServerimplementationandaclientthatcanconnecttothatserver.Inthissection,we'lldeveloptheservertheclientwillbedevelopedinthe
nextsection.We'lldevelopaserverthatcanserveasthebeginningofamultiplayertypinggame:multipleclientsconnecttotheserver,whichsendsthesame
stringtoeachclientandkeepstrackofalltheirscores.We'lldeveloponlythefirstpartofthatserver,thepartthatsendsastringtoeachclient.Theremaining
logiccontainsnonewinformationaboutthreading,sowe'llleaveitasanexerciseforthereader.
Developingaserverlikethisdependsonestablishingaprotocolbetweentheclientandserver.Forourexample,weuseasimpleprotocolwheremessagesare
asinglebyte(themessagetype),optionallyfollowedbydataspecifictothemessagetype.Wedefinethreetypesofmessages:
packagejavathreads.examples.ch12;
publicclassTypeServerConstants{
publicfinalstaticbyteWELCOME=0;
publicfinalstaticbyteGET_STRING_REQUEST=1;
publicfinalstaticbyteGET_STRING_RESPONSE=2;
}
TheWELCOMEmessagemustbesentbytheserverwhenitacceptsanewclientintothegameithasnooptionaldata.TheGET_STRING_REQUESTmessage
issentbytheclientwhenitwantsanewstringittoohasnooptionaldata.Finally,theGET_STRING_RESPONSEmessageissentbytheserverwhenithas
processedaGET_STRING_REQUESTitmustbefollowedbyaUTF8encodedstringthattheclientisexpectedtotype.
Here'stheimplementationofourserver:
packagejavathreads.examples.ch12.example1;
importjava.io.*;
importjava.net.*;
importjavathreads.examples.ch12.*;
publicclassTypeServerextendsTCPServer{
publicvoidrun(Socketdata){
try{
DataOutputStreamdos=
newDataOutputStream(data.getOutputStream());
dos.writeByte(TypeServerConstants.WELCOME);
DataInputStreamdis=
newDataInputStream(data.getInputStream());
while(true){
byteb=dis.readByte();
if(b!=TypeServerConstants.GET_STRING_REQUEST){
System.out.println("Clientsentunknownrequest"+b);
continue;
}
dos.writeByte(TypeServerConstants.GET_STRING_RESPONSE);
dos.writeUTF("Thisisateststring");
dos.flush();
}
}catch(Exceptione){
System.out.println("Clientterminating:"+e);
return;
}
}
publicstaticvoidmain(String[]args)throwsIOException{
TypeServerts=newTypeServer();
ts.startServer(Integer.parseInt(args[0]));
System.out.println("Serverreadyandwaiting...");
}
}
Rememberthattherun()methodinthisclassiscalledafteranewconnectionhasbeenmade(andwithinanewthread).Itwritesoutthewelcomemessage
andthensimplyloops.EachtimeitexecutesthereadByte()method,itblocksuntiltheclientsendstheactualrequestforastring.That'sthereasonwhy
we'rerunningtheclientinaseparatethreadotherclientsexecutethereadByte()methodoncompletelyseparatesocketsinseparatethreads.Whena
messageisreceived,thestringtotypeissentbackintheproperUTF8encodedformat.Thestringhereisalwaysthesame,butyoucouldgeneraterandom
stringsinyourserver.
Thisclassisalsoresponsibleforstartingtheserver,whichisasimplecaseofinstantiatingtheserverobjectandcallingitsstartServer()method.Note
thatthemainthreadthenexits,dependingonthethreadstartedbythestartServer()methodtocontinueallthework.We'venotprovidedanywaytostop
theserverotherthankillingtheentireprocess,althoughwe'llexploresomewaystodothatinlaterexamples.
importjava.net.*;
importjava.io.*;
importjava.util.*;
importjava.util.concurrent.*;
importjava.util.concurrent.locks.*;
importjavathreads.examples.ch12.*;
publicclassRandomCharacterGeneratorextendsThreadimplementsCharacterSource{
privatechar[]chars;
privateintcurChar;
privateRandomrandom=newRandom();
privateCharacterEventHandlerhandler;
privatebooleandone=true;
privateLocklock=newReentrantLock();
privateConditioncv=lock.newCondition();
privateSocketsock;
privateDataInputStreamreader;
privateDataOutputStreamwriter;
publicRandomCharacterGenerator(Stringhost,intport)throwsIOException{
handler=newCharacterEventHandler();
sock=newSocket(host,port);
reader=newDataInputStream(sock.getInputStream());
reader.read();//Welcome
writer=newDataOutputStream(sock.getOutputStream());
getString();
}
privatesynchronizedvoidgetString()throwsIOException{
byteb=TypeServerConstants.GET_STRING_REQUEST;
writer.write(b);
writer.flush();
b=(byte)reader.readByte();
if(b!=TypeServerConstants.GET_STRING_RESPONSE)
thrownewIllegalStateException("Badrecvstate"+b);
Strings=reader.readUTF();
chars=s.toCharArray();
curChar=0;
}
publicintgetPauseTime(intminTime,intmaxTime){
return(int)(minTime+((maxTimeminTime)*random.nextDouble()));
}
publicintgetPauseTime(){
returngetPauseTime(2000,5500);
}
publicvoidaddCharacterListener(CharacterListenercl){
handler.addCharacterListener(cl);
}
publicvoidremoveCharacterListener(CharacterListenercl){
handler.removeCharacterListener(cl);
}
publicvoidnextCharacter(){
handler.fireNewCharacter(this,
(int)chars[curChar++]);
if(curChar==chars.length){
try{
getString();
}catch(IOExceptionioe){
//Putupadialogboxtoalertuseroferror
}
}
}
publicvoidrun(){
try{
lock.lock();
while(true){
try{
if(done){
cv.await();
}else{
nextCharacter();
cv.await(getPauseTime(),TimeUnit.MILLISECONDS);
}
}catch(InterruptedExceptionie){
return;
}
}
}finally{
lock.unlock();
}
}
publicvoidsetDone(booleanb){
try{
lock.lock();
done=b;
if(!done)cv.signal();
}finally{
lock.unlock();
}
}
}
Theonlythreadhereistheonewe'vealwayshad,whichsendsoutthenextcharacterfromthestringretrievedfromtheserver.Asimpleextensionforyour
ownpracticewouldbetousetheapproachfromChapter7andshowconnectionprogressinthemainSwingapplication.
theservercanhandle.First,theservercanstartonlyacertainnumberofthreads.Thatnumberdependsontheoperatingsystemhostingtheserver,the
amountofmemoryavailabletotheserver,andsoon,butthenumberofthreadsthatatypicalservercanhandleisfarlessthanthenumberofsocketsitcould
otherwisehandle.ThesecondlimithastodowiththethroughputoftheserveraswesawinChapter10,withtoomanyactivethreads,thetotalthroughputof
theprogramsuffers.Evenifyouhaveenoughmemorytohandlethousandsofthreads,youdon'twantthemalltousetheCPUatthesametime,orall
requeststakeaverylongtime.
Toaddresstheseconcerns,let'slookathowtolimitthenumberofthreadsthathandleI/Ointheserver.UsingtraditionalI/O,wecansetupapoolofthreads
tohandlerequeststhisplacesanupperlimitonthenumberofsimultaneousclientrequeststhatwecanhandle.Oursecondexampleshowstheserverand
clientcodetousewhenyouwanttothrottlethenumberofthreads.
Thisapproachworksonlyforapplicationsinwhichtheclientconnectionsareshortlived.Itdependsonthefactthatthethreadsintheserverdonotblock
becausetheydonotreaddatafromtheclient(otherthantheinitialrequest,whichistypicallyavailableassoonastheclienthasmadeaconnection).This
approachcanalsoworkifyoudon'tcarewhethernewclientsarenotalwaysabletoconnect.Ifyousetanupperlimitof,say,200clientsanddon'tmindthat
clientnumber201hastowaitanindeterminateamountoftimeforapreviousclienttoexit,youcanusetheexampleinthissection.Otherwise,ifthescaling
issuesoftraditionalI/Oareaproblemforyourapplication,lookatthenewI/Otechniquesdescribedinthenextsectionofthischapter.
Thedesignpatternofthisexampleisknownastheleaderfollowerpattern.Itreliesonthefactthatonlyonethreadcanexecutetheaccept()methodthatis,
theinternalimplementationoftheaccept()methodissynchronized.Thethreadthatobtainsthatlockcanestablishtheconnectionwithaclientandobtain
thatclient'sdatasocket.Itcanthenreleasethelock,andthenextthreadinlinethenobtainsthelockandprocessesthenextclient.
Tousethispattern,wemustextendourTCPServerclass:
packagejavathreads.examples.ch12;
importjava.net.*;
importjava.io.*;
publicabstractclassTCPThrottledServerimplementsRunnable{
ServerSocketserver=null;
Thread[]serverThreads;
volatilebooleandone=false;
publicsynchronizedvoidstartServer(intport,intnThreads)
throwsIOException{
server=newServerSocket(port);
serverThreads=newThread[nThreads];
for(inti=0;i<nThreads;i++){
serverThreads[i]=newThread(this);
serverThreads[i].start();
}
}
publicsynchronizedvoidsetDone(){
done=true;
}
publicvoidrun(){
while(!done){
try{
Socketdata;
data=server.accept();
run(data);
}catch(IOExceptionioe){
System.out.println("Accepterror"+ioe);
}
}
}
publicvoidrun(Socketdata){
}
}
Noticethatourimplementationisnowmuchsimplerbecausewenolongerneedtocreatethreadsonthefly.Weestablishafixednumberofthreadsinthe
startServer()method.Eachthreadexecutestherun()method,whereeachinturngetsaclientdatasocket.Becausethethreaditselfoperatesonthe
socket,theserverobjectnolongerneedstocloneitselfitcansimplycalltherun(Socketdata)method.Theonlyothersignificantchangeisthatthe
startServer()methodmustnowkeeptrackofallthethreadssothatthestopServer()methodcaninterruptthethreads.
OuractualTypeServerimplementationisverysimilartoitspreviousincarnation,exceptthatitnowcanreadonlyasingleclientrequest:
packagejavathreads.examples.ch12.example2;
importjava.io.*;
importjava.net.*;
importjavathreads.examples.ch12.*;
publicclassTypeServerextendsTCPThrottledServer{
publicvoidrun(Socketdata){
try{
DataOutputStreamdos=
newDataOutputStream(data.getOutputStream());
dos.writeByte(TypeServerConstants.WELCOME);
DataInputStreamdis=
newDataInputStream(data.getInputStream());
byteb=dis.readByte();
if(b!=TypeServerConstants.GET_STRING_REQUEST){
System.out.println("Clientsentunknownrequest"+b);
return;
}
dos.writeByte(TypeServerConstants.GET_STRING_RESPONSE);
dos.writeUTF("Thisisateststring");
dos.flush();
}catch(Exceptione){
System.out.println("Clientterminating:"+e);
return;
}finally{
try{
data.close();
}catch(IOExceptionioe){
}
}
}
publicstaticvoidmain(String[]args)throwsIOException{
TypeServerts=newTypeServer();
ts.startServer(Integer.parseInt(args[0]),Integer.parseInt(args[1]));
System.out.println("Serverreadyandwaiting...");
}
}
Handlingonlyasinglerequesthassimplifiedthisimplementationaswell.Thereisnofreelunchhowever:theRandomCharacterGeneratorclassis
nowmorecomplicatedbecauseitcannolongerkeepitsconnectiontotheserveropen.Instead,eachtimeitwantsanewstring,itmustmakeanew
connectiontotheserver:
packagejavathreads.examples.ch12.example2;
...
publicclassRandomCharacterGeneratorextendsThreadimplementsCharacterSource{
...
publicRandomCharacterGenerator(Stringhost,intport){
handler=newCharacterEventHandler();
this.host=host;
this.port=port;
}
privatesynchronizedvoidgetString()throwsIOException{
Socketsock=newSocket(host,port);
DataInputStreamreader=newDataInputStream(sock.getInputStream());
reader.read();//Welcome
DataOutputStreamwriter=newDataOutputStream(sock.getOutputStream());
byteb=TypeServerConstants.GET_STRING_REQUEST;
writer.write(b);
writer.flush();
b=(byte)reader.readByte();
if(b!=TypeServerConstants.GET_STRING_RESPONSE)
thrownewIllegalStateException("Badrecvstate"+b);
Strings=reader.readUTF();
chars=s.toCharArray();
curChar=0;
sock.close();
}
...
}
Continuallymakingnewconnectionstotheservercanbeanuisance,aswellashavingperformanceimplications:ittakesasignificantamountoftimetoset
upasocketconnection.Iftheprotocolofyourapplicationissuchthatmessagesflowfrequentlybetweenclientandserver,thisimplementationisinefficient.
Forapplicationsthathandlealargenumberofclientsmakingsinglerequests,however,thisisagoodwaytoscaleyourserverusingtraditionalI/O.
Nonblocking I/O
Tounderstandthecomplexitieswe'refacing,let'scompareblockingandnonblockingI/O.OurprogramreadsaUTFencodedstring.Thatstringis
representedasaseriesofbytes.Thefirstfourbytesmakeupanintegerthatindicateshowmuchdatathestringcontains.Theremainingdataischaracterdata,
therepresentationofwhichdependsonthelocaleinwhichthedataisproduced.Thedatarepresentationforthestring"Thisisateststring"appearsinFigure
122.Thefirstfourbytestellusthatthestringhas17characters,andthenext17bytesaretheASCIIrepresentationofthatstring.
Figure122.ByterepresentationofaUTFencodedstring
Anapplicationthatwantstoreadthisstringfirstrequests2bytes,calculatesthelength,andthenrequests17bytes.
Asthisdatatravelsoverthenetwork,itmaybecomefragmented.Dataonanetworkissentinpackets,andeachpackethasamaximumsizethatitcan
accommodate.It'spossible,then,forthefirstpartofthedatatoarrivemuchsoonerthanthesecondpartofthedata.Inthecaseofanetworkfailure(oran
extremelyilltimedcomputerfailureonthesendingmachine),thesecondpartofthedatamayneverarrive.Therefore,whentheapplicationrequeststhe17
bytes,itmaygetbackonlythefewbytesthathavealreadyarrived(thesameistruewhenitrequeststhe2bytes).Theapplicationmustthenrequestmoredata
tocompletereadingthestring.
ThedifferencebetweenblockingandnonblockingI/Oisinhowthissituationishandled.WithblockingI/O,thereadUTF()methodcanjustrequestthe
additionaldata.Requestingthatdatablocksuntilthedatafinallymakesitswaytothemachine,atwhichpointthereadUTF()methodcancompleteits
constructionofthestringandreturnthatstringtotheuser.
WithnonblockingI/O,thatsolutiondoesn'twork.Whenamethodattemptstoreaddataandnoneisavailable,themethodimmediatelyreturnswithan
indicationthatnodatawaspresent.Youcan'timmediatelyretryreadingthedatabecauseitstillmaynotbeavailable,andyou'dendupcontinuallywasting
CPUcyclesasyouattempttoreadthenonexistentdata.Worse,you'dloseanybenefitofnonblockingI/O:ifyou'regoingtoreaddatauntileverythingis
ready,youmayaswellusetraditional,blockingI/O.
WhenyouusenonblockingI/O,then,it'syourresponsibilitytobepreparedforthissituationandcopewiththefactthatallthedatayouneedtoprocessmay
notbeimmediatelyavailable.It'sthisprogrammingthatmakesnonblockingI/Omoredifficulttouse.
Thissituationisn'tlimitedtoreadingdatafromsockets.Whenyouwritedatatosockets,thedatayou'rewritingisbufferedintheoperatingsystemuntilthe
OScanputthedataonthenetwork.Ifthenetworkisverybusy,theOSbuffermayfillup,andyouwon'tbeabletowriteanydatatoit.Worse,youmay
attempttowrite100bytes,buttheOSbuffersmayhaveonly64bytesavailable:you'llendupwritingthefirst64bytes,butthenyoumustgobacklaterand
writetheremaining36bytes.
FileI/Ocanhaveasimilarproblem.Whenyou'rereadingdatafromthedisk,theoperatingsystemmayhavetoretrievetheactualdatafrommanydifferent
locationsonthedisk.Asaresult,someofthedatamaybeavailableimmediatelywhiletheremainingdatamaynotbeavailableuntilthediskcompletesits
rotationtothecorrectspotfortheoperatingsystemtoreadit.Inwritingdata,youmayfacethesameproblem:youmaywritefasterthantheoperatingsystem
canflushitsbufferstodisk,inwhichcase,you'llhaveapartialwriteofyourdata.
Thesituationswe'vedescribedhereareverysimilartoaraceconditiontheydependonacertainsequenceofeventsoccurringinaparticularorder.Itturnsout
thattheyarejustasrare.It'spossibletowriteaserverorotherprogramusingnonblockingI/Oandalwaysassumewhenyoureaddatathatyou'llread
everythingyouneedandwhenyouwritedatathatitwillallgetwrittencorrectly.Suchaprogramwillworkalmostallthetime.Almost.
importjava.net.*;
importjava.io.*;
importjava.nio.channels.*;
importjava.util.*;
publicabstractclassTCPNIOServerimplementsRunnable{
protectedServerSocketChannelchannel=null;
privatebooleandone=false;
protectedSelectorselector;
protectedintport=8000;
publicvoidstartServer()throwsIOException{
channel=ServerSocketChannel.open();
channel.configureBlocking(false);
ServerSocketserver=channel.socket();
server.bind(newInetSocketAddress(port));
selector=Selector.open();
channel.register(selector,SelectionKey.OP_ACCEPT);
}
publicsynchronizedvoidstopServer()throwsIOException{
done=true;
channel.close();
}
protectedsynchronizedbooleangetDone(){
returndone;
}
publicvoidrun(){
try{
startServer();
}catch(IOExceptionioe){
System.out.println("Can'tstartserver:"+ioe);
return;
}
while(!getDone()){
try{
selector.select();
}catch(IOExceptionioe){
System.err.println("Servererror:"+ioe);
return;
}
Iteratorit=selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKeykey=(SelectionKey)it.next();
if(key.isReadable()||key.isWritable()){
//Keyrepresentsasocketclient
try{
handleClient(key);
}catch(IOExceptionioe){
//Clientdisconnected
key.cancel();
}
}elseif(key.isAcceptable()){
try{
handleServer(key);
}catch(IOExceptionioe){
//Accepterror;treatasfatal
thrownewIllegalStateException(ioe);
}
}elseSystem.out.println("unknownkeystate");
it.remove();
}
}
}
protectedvoidhandleServer(SelectionKeykey)throwsIOException{
SocketChannelsc=channel.accept();
sc.configureBlocking(false);
sc.register(selector,SelectionKey.OP_READ);
registeredClient(sc);
}
protectedabstractvoidhandleClient(SelectionKeykey)throwsIOException;
protectedabstractvoidregisteredClient(SocketChannelsc)throwsIOException;
}
OurintenthereisnottoexplainingreatdetailtheNIOclassesthemselvesforagoodreference,seeJavaNIO(O'Reilly).Fromathreadingperspective,this
istheclassicapproachtoasinglethreadedserverthanhandlesmultipleclients.Theselectorkeepstrackoftwothings:therendezvoussocketandallopen
clientsockets.Whenanyofthosesocketshavedataavailable,theselectorisnotified,andthesetofsocketswithpendingdataisreturnedviathe
selectedKeys()method.Ourserveriteratesovereachsocketinthatset.Ifthesocketistherendezvoussocket,thehandleServer()methodiscalled,a
newclientconnectionismade,andtheclientsocketisregisteredwiththeselector.Otherwise,thesocketisaclientdatasocket,andthehandleClient()
methodiscalled.
ThereasonwecandothisallinasinglethreadisthattheI/OthatoccursinthehandleClient()andhandleServer()methodsneverblocks.
Consequently,oursinglethreadneverblocksevenwiththousandsofclientsocketswithpendingI/O,eachishandledinturn.
Asbefore,weneedtoprovideasubclassofthisframeworkthathandlestheactualclientdata.Here'showwe'dwriteasubclassbasedonourtypingserver
protocol:
packagejavathreads.examples.chio.example3;
importjava.io.*;
importjava.nio.*;
importjava.nio.channels.*;
importjava.nio.charset.*;
importjava.net.*;
importjava.util.*;
importjavathreads.examples.chio.*;
publicclassTypeServerextendsTCPNIOServer{
staticStringtestString="Thisisateststring";
staticclassClientInfo{
ByteBufferinBuf=ByteBuffer.allocateDirect(512);
ByteBufferoutBuf=ByteBuffer.allocateDirect(512);
booleanoutputPending=false;
SocketChannelchannel;
}
MapallClients=newHashMap();
Charsetencoder=Charset.forName("UTF8");
protectedvoidhandleClient(SelectionKeykey)throwsIOException{
SocketChannelsc=(SocketChannel)key.channel();
ClientInfoci=(ClientInfo)allClients.get(sc);
if(ci==null)
thrownewIllegalStateException("Unknownclient");
if(key.isWritable())
send(sc,ci);
if(key.isReadable())
recv(sc,ci);
}
privatevoidrecv(SocketChannelsc,ClientInfoci)throwsIOException{
ci.channel.read(ci.inBuf);
ByteBuffertmpBuf=ci.inBuf.duplicate();
tmpBuf.flip();
intbytesProcessed=0;
booleandoneLoop=false;
while(!doneLoop){
byteb;
try{
b=tmpBuf.get();
}catch(BufferUnderflowExceptionbue){
//Processedalldatainbuffer
ci.inBuf.clear();
doneLoop=true;
break;
}
switch(b){
caseTypeServerConstants.WELCOME:
bytesProcessed++;
break;
caseTypeServerConstants.GET_STRING_REQUEST:
bytesProcessed++;
if(ci.outputPending){
//Clientisbackedup.Wecan'tappendto
//thebytebufferbecauseit'sinthewrong
//state.Wecouldallocateanotherbuffer
//hereandchangeoursendmethodtoknow
//aboutmultiplebuffers,butwe'lljust
//assumethattheclientisdead
break;
}
ci.outBuf.put(TypeServerConstants.GET_STRING_RESPONSE);
ByteBufferstrBuf=encoder.encode(testString);
ci.outBuf.putShort((short)strBuf.remaining());
ci.outBuf.put(strBuf);
ci.outBuf.flip();
send(sc,ci);
break;
caseTypeServerConstants.GET_STRING_RESPONSE:
intstartPos=tmpBuf.position();
try{
intnBytes=tmpBuf.getInt();
byte[]buf=newbyte[nBytes];
tmpBuf.get(buf);
bytesProcessed+=buf.length+5;
Strings=newString(buf);
//SendthestringtotheGUI
break;
}catch(BufferUnderflowExceptionbue){
//Processedallavailabledata
ci.inBuf.position(ci.inBuf.position()+bytesProcessed);
doneLoop=true;
}
break;
}
}
}
privatevoidsend(SocketChannelsc,ClientInfoci)throwsIOException{
intlen=ci.outBuf.remaining();
intnBytes=sc.write(ci.outBuf);
if(nBytes!=len){
//Clientnotreadytoreceivedata
ci.outputPending=true;
ci.channel.register(selector,
SelectionKey.OP_READ|SelectionKey.OP_WRITE);
}
else{
ci.outBuf.clear();
if(ci.outputPending){
ci.outputPending=false;
ci.channel.register(selector,SelectionKey.OP_READ);
}
}
}
protectedvoidregisteredClient(SocketChannelsc)throwsIOException{
ClientInfoci=newClientInfo();
ci.channel=sc;
ci.outBuf.clear();
ci.outBuf.put(TypeServer.WELCOME);
ci.outBuf.flip();
allClients.put(sc,ci);
send(sc,ci);
}
publicstaticvoidmain(String[]args)throwsException{
TypeServerts=newTypeServer();
ts.port=Integer.parseInt(args[0]);
Threadt=newThread(ts);
t.start();
System.out.println("Typeserverready...TypeCTRLDtoexit");
while(System.in.read()>0)
;
ts.stopServer();
t.join();
}
}
NotethegreatlyincreasedcomplexityinthisexamplefromourmultithreadedblockingI/Oexample:that'sthepricewehavetopaytohandlealltheadditional
clients.Intherecv()method,we'rereadingallthedataavailablefromaclient.Thatisusuallyjustasinglerequest,but,infact,nothingpreventstheclient
fromsendingmultiplerequestsatthesametime.Therefore,wemustbereadytoprocessalltheavailabledata,whichiswhywesetuptheouterloopthat
attemptstoreadaseriesofrequests.
Ourrequestsareasinglebytelong,sowhenI/Oisavailable,weknowthatthere'satleastonerequest.However,somemessageshaveadditionaldata.The
GET_STRING_RESPONSEmessageconsistsofthesinglebyteindicatingthemessagetypeandtheUTFencodedstring.Noticehowwereadthisfroma
temporarybufferincaseallthedataisn'tpresent:ifinprocessingthedatawefindthatitisn'tallthere,wecanjustdiscardthetemporarybuffer.Thenexttime
therecv()methodiscalled(whichhappenswhenwe'vereceivedatleastsomeoftheremainingdata),thatdataisappendedtothebufferandwetryto
processitagain.
Inthesend()method,wealsochecktomakesurethatwe'vewrittenallthedata.Ifnot,wehavetochangeourselectioncriteria.We'renotinterestingin
knowingwhetherthesocketcanacceptdataunlessweactuallyhavependingdatatosendtoit,sothat'stheonlytimeweasktobesignaledforOP_WRITE.
importjava.util.concurrent.*;
importjava.io.*;
importjava.nio.*;
importjava.nio.channels.*;
importjavathreads.examples.ch12.*;
publicclassCalcServerextendsTCPNIOServer{
staticThreadPoolExecutorpool;
classFibClassimplementsRunnable{
longn;
SocketChannelclientChannel;
ByteBufferbuffer=ByteBuffer.allocateDirect(8);
FibClass(longn,SocketChannelsc){
this.n=n;
clientChannel=sc;
}
privatelongfib(longn){
if(n==0)
return0L;
if(n==1)
return1L;
returnfib(n1)+fib(n2);
}
publicvoidrun(){
try{
longanswer=fib(n);
buffer.putLong(answer);
buffer.flip();
clientChannel.write(buffer);
if(buffer.remaining()>0){
Selectors=Selector.open();
clientChannel.register(s,SelectionKey.OP_WRITE);
while(buffer.remaining()>0){
s.select();
clientChannel.write(buffer);
}
s.close();
}
}catch(IOExceptionioe){
System.out.println("Clienterror"+ioe);
}
}
}
protectedvoidhandleClient(SelectionKeykey)throwsIOException{
SocketChannelsc=(SocketChannel)key.channel();
ByteBufferbuffer=ByteBuffer.allocateDirect(8);
sc.read(buffer);
buffer.flip();
longn=buffer.getLong();
FibClassfc=newFibClass(n,sc);
pool.execute(fc);
}
protectedvoidregisteredClient(SocketChannelsc){
}
publicstaticvoidmain(String[]args)throwsException{
CalcServercs=newCalcServer();
cs.port=Integer.parseInt(args[0]);
inttpSize=Integer.parseInt(args[1]);
pool=newThreadPoolExecutor(
tpSize,tpSize,50000L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue<Runnable>());
cs.run();
System.out.println("Calcserverwaitingforrequests...");
}
}
Fromathreadingperspective,theinterestingthingtonotehereisthatthehandlingofnonblockingI/Oissomewhateasier.Becausetherearenowmultiple
threads,wecanaffordtowaitifoneparticularclientisdelayedinreadingorwritingdata.Oneofourthreadsmightperiodicallyblocknow(butonlyfor60
[ 1 ]
seconds),butthatwon'tgreatlyaffecttheoverallthroughputofourserver.
OnepointaboutusingmultiplethreadsandthenewI/Oclasses:thebuffersandchannelsoftheseclassesaretypicallynotthreadsafe.That'snotusuallya
problembecausethepointoftheexerciseistohandleeachchannelinaseparatethread(oreverythinginasinglethread).
Interrupted I/O
InChapter2,weintroducedtheinterrupt()method,whichinterruptsathreadthatisblockedinasleep(),wait(),join(),orsimilarmethod.The
interrupt()methodalsosetsaflaginthethreadthatisfrequentlyusedasasignaltothethreadthatitshouldterminate.
TraditionalI/OmethodsinJavacanalsoblock:we'veseenhowreadingfromasocketisablockingmethod.Theaccept()methodoftheServerSocket
classisinherentlyblockingsocketconstructorsmayblockwhiletheconnectionisestablished,and,undersomecircumstances,writingtoasocketmay
block.FileI/Ocanalsoblock,thoughmuchmorerarely(althoughifthefileisfromanetworkfileserver,blockingbecomesmorelikely).
Whatistheeffectofcallinginterrupt()onathreadthatisblockedinI/O?Theanswertothatisplatformdependent.OnUnixoperatingsystemssuchas
SolarisandLinux,theinterrupt()methodcausestheblockedI/OmethodtothrowanInterruptedIOException.Unfortunately,Windows
operatingsystemsdonotsupportinterruptibleI/O,soonthoseplatformsathreadblockedonanI/Omethodremainsblockedafterithasbeeninterrupted.
Sowhat'saprogrammertodo?Thesafestanswerisnottorelyontheinterrupt()methodtounblockathreadthatiswaitingforI/Otocomplete:ifyou
needtounblocksuchathread,youshouldclosetheinputoroutputstreamonwhichthethreadisblocked.IfinterruptibleI/Oasagenericfeatureisaddedto
Javainthefuture,itwilllikelyhaveadifferentinterfacethanthemethodthrowinganInterruptedIOException.
IfyoudorelyoninterruptibleI/O,beawarethattheI/Oinquestionisnotrestartable:it'simpossibletodeterminethestateoftheI/Oandknowatwhichpoint
itshouldstartagain.ThedifficultyofdealingwiththeissueofrestartingI/Othathasbeeninterruptedisaprimereasonwhyitsimplementationisinconsistent
betweenoperatingsystems.
Undercertaincircumstances,youcanstillusetheinterrupt()methodtoclosedownanI/Othreadonallplatforms.Thiscanworkif,whenyoucallthe
interrupt()method,youintendtoclosetheinputstreaminquestionsinceclosingtheinputstreamunblocksthethreadonallplatforms.
Thisabstractclassdemonstratesthisprinciple:
packagejavathreads.examples.ch12;
importjava.net.*;
importjava.io.*;
publicabstractclassInterruptibleReaderextendsThread{
privateObjectlock=newObject();
privateInputStreamis;
privatebooleandone;
privateintbuflen;
protectedvoidprocessData(byte[]b,intn){}
classReaderClassextendsThread{
publicvoidrun(){
byte[]b=newbyte[buflen];
while(!done){
try{
intn=is.read(b,0,buflen);
processData(b,n);
}catch(IOExceptionioe){
done=true;
}
}
synchronized(lock){
lock.notify();
}
}
}
publicInterruptibleReader(InputStreamis){
this(is,512);
}
publicInterruptibleReader(InputStreamis,intlen){
this.is=is;
buflen=len;
}
publicvoidrun(){
ReaderClassrc=newReaderClass();
synchronized(lock){
rc.start();
while(!done){
try{
lock.wait();
}catch(InterruptedExceptionie){
done=true;
rc.interrupt();
try{
is.close();
}catch(IOExceptionioe){}
}
}
}
}
}
Whatwe'vedoneinthisclassistostarttwothreads:onethatisreadingthedataandonethatiswaitingforaninterrupttooccur.Whenthewaitingthreadis
interrupted,itclosestheinputstreamthatthereadingthreadisblockedon,andboththreadsthenexit.Thisallowsustoshutdownthethread(andclosethe
inputstreamassociatedwiththethread)byinterruptingthewaitingthread:
InterruptibleReaderir=...someconcretesubclassofinterruptiblereader...;
...Dootherthingsuntilweneedtoshutdownthereader...
ir.interrupt();
Aconcreteimplementationoftheinterruptiblereadermightlooklikethis:
packagejavathreads.examples.ch12.example5;
importjava.net.*;
importjava.io.*;
importjavathreads.examples.ch12.*;
publicclassInterruptibleClientextendsInterruptibleReader{
publicvoidprocessData(byte[]b,intn){
System.out.println("Gotdata"+newString(b,0,n));
}
publicInterruptibleClient(InputStreamis){
super(is);
}
publicstaticvoidmain(String[]args)throwsException{
Sockets=newSocket(args[0],Integer.parseInt(args[1]));
InputStreamis=s.getInputStream();
InterruptibleClientc=newInterruptibleClient(is);
c.start();
System.out.println("Mainthreadsleeping");
Thread.sleep(10000);
System.out.println("Mainthreadwokeup");
c.interrupt();
System.out.println("Mainthreadcalledinterrupt");
}
}
Ratherthangoingtoallthiseffort,wemightsimplyhaveclosedtheinputstreamdirectly.Similarly,wemighthavewrittenashutdown()methodinthe
InterruptibleReaderclassthatclosedtheinputstream(whichwouldhavesavedusathread).Thereasonyoumightselectthisapproachisthatitkeeps
thingsconsistentamongallthreads:youcanusetheinterrupt()methodtostopallofthem.Chapter13describeshowyoucanarrangetointerrupta
groupofthreadsatonce,whichisanotheradvantagetothisapproach.
Summary
UsingmultiplethreadswellisveryimportantinanyJavaprogramthatperformsalotofI/O.Inthesimplestcase,I/O(andparticularlysocketI/O)mayblock
atanypointintimeifyouwanttomakesurethatyourprogramremainsresponsivewhileperformingI/O,youmustperformtheI/Oinanotherthread.For
simplecases,thismeanshavingasinglethreadforeveryI/Osourceyou'reinterestedin.
ThatmodeldoesnotscalecompletelyasthenumberofI/Osourcesgrows.Atthispoint,youmustbegintolookatotherthreadingsolutions.Onesolutionis
tocontinuetouseblockingI/Obuttolimitthenumberofthreadsactiveatanytime.Althoughthatsolutionhaslimitedapplicability,it'sasimpleextensionto
abasicidea.
Inmostothercases,you'llneedtousethenonblockingfeaturesofJava'sNIOclasses.Althoughtheseclassesincreasethecomplexityofyourapplications,
theyallowyoutohandlemanyI/Osourceswithasinglethread.ThecomplexityofusingnonblockingI/Ocanbemitigatedsomewhatbyusingmultiple
threadswithnonblockingI/OthatsolutionisalsoappropriatewhenyouhavemultipleCPUsavailabletoprocessrequestsorwhentherequeststhemselves
needtoblockforotherreasons.
Usedjudiciously,Java'sthreadingandI/Omodelsallowyougreatflexibilityindevelopingcomplexprograms.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
SingleThreadedServer
javathreads.examples.ch12.example1.TypeServerportNumber
ch12ex1
server
SingleThreadedClient
javathreads.examples.ch12.example1.SwingTypeTesterhostnameportNumber
ch12ex1
client
ThrottledServer
javathreads.examples.ch12.example2.TypeServerportNumber
ch12ex2
server
ThrottledClient
javathreads.examples.ch12.example2.SwingTypeTesterhostnameportnumber
ch12ex2
client
NIOSingleThreaded
javathreads.examples.ch12.example3.TypeServerportNumber
Server
MultithreadedCalcServer
ch12ex3
server
javathreads.examples.ch12.example4.CalcServerportNumbernServerThreads
ch12ex4
server
CalcClient
InterruptibleClient
javathreads.examples.ch12.example4.CalcServernClientRequestsFibNumberhostname
ch12ex4
portNumber
client
javathreads.examples.ch12.example5.InterruptibleClienthostnameport
ch12ex5
client
Thesinglethreadedclient(example1)canbeusedwitheithersinglethreadedserver(examples1and3).Theinterruptibleclientcanbeusedwithanytypeof
server.TochangeportsandhostnamesfortheAnttargets,usetheseproperties:
<propertyname="TypeServerHost"value="localhost"/>
<propertyname="TypeServerPort"value="8003"/>
<propertyname="TypeServerNThreads"value="10"/>
<propertyname="CalcServerNThreads"value="5"/>
<propertyname="CalcClientNThreads"value="10"/>
<propertyname="CalcServerFibNumber"value="20"/>
<propertyname="CalcServerHost"value="localhost"/>
<propertyname="CalcServerPort"value="8003"/>
[ 1 ]
WetookashortcutwherewereadbytesintheexampleabovebecauseweknowhowTCPtrafficworks.Butingeneral,thereadcase
needstobehandledsimilarlytothewritecasetomakesureyouactuallyreadtheamountofdatayouexpect.
Thread Groups
Allthreadsbelongtoathreadgroup,which,asitsnameimplies,isagroupofthreads.Threadgroupsaredefinedbythejava.lang.ThreadGroupclass.
Althoughwehaven'tyetmentionedthem,threadgroupshavebeenaroundallalong.Everythreadyoucreateautomaticallybelongstoadefaultthreadgroup
thattheJavavirtualmachinesetsuponyourbehalf.Everythreadwe'velookedatsofarbelongstothisexistingthreadgroup,whichisknownasthe"main"
threadgroup.
Thevirtualmachinealsohasa"system"threadgroup.Thisthreadgroupcontainsthethreadsthathandlefinalizationandweakreferences.Thisgroupdoes
notcontainallthreadsofthevirtualmachine:somesystemlevelthreads(suchasthegarbagecollectionthread(s))havenocorrespondingJavathreadobject
anddonotexistinanyJavathreadgroup.
Threadgroupsaremorethanjustarbitrarygroupingsofthreadstheyarerelatedtoeachother.Everythreadgrouphasaparentthreadgroup,sothreadgroups
existinatreehierarchy.Theobviousexceptiontothis,ofcourse,istherootofthetree,whichisknownastherootthreadgrouporthesystemthreadgroup.
[ 1 ]
EveryJavaprogramhasbydefaulttwothreadgroups:thesystemthreadgroupcontainsthethreadsofsomesystemleveltasks.
Thesystemthread
grouphasonechild,themainthreadgroup,whichcontainsthethreadthatstartsyourprogram,theAWTeventdispatchingthread,anydefaultthreadyou
create,andanythreadsstartedbytheJavaAPI.Figure131showsasamplethreadhierarchyfromasystemrunningtheJavaPlugin.Inthisfigure,each
appletisgivenitsownthreadwhichisstartedinitsownthreadgroup.Someoftheappletshavecreatedadditionalthreadgroupstocompletethehierarchy
shown.
Figure131.An(incomplete)threadgrouphierarchy
Youcancreateyourownthreadgroupsaswellandmakethishierarchyarbitrarilycomplex.ThreadgroupsarecreatedjustlikeanyJavaobjectwhenyou
instantiateathreadgroupobject,youspecifyitsparentthreadgroupinthehierarchy(bydefault,theparentthreadgroupisthecurrentthreadgroup).When
youinstantiateaThreadobject,youmayoptionallyspecifythethreadgrouptowhichitshouldbelong.Ifyoudon'tspecifyathreadgroup,oneoftwothings
happens:
Ifasecuritymanagerhasbeeninstalled,thegetThreadGroup()methodofthesecuritymanageriscalledandthethreadjoinsthegroupreturnedby
thatmethod.
Otherwise,thethreadjoinsthecurrentthreadgroup(thethreadgroupoftheinstantiatingthread).
Threadgroupshavetwoadvantages.First,conveniencemethodsofthethreadgroupclassallowyoutooperateonallthreadsinthegroup.If,forexample,
youwantedtointerruptallthreadsinaparticulargroup,youcouldcalltheinterrupt()methodonthethreadgroupobject,anditwouldcallthe
interrupt()methodofeachofitsthreads.Theinterrupt()methodisreallytheonlymethodoftheThreadGroupclassthatcanaffectallthethreads
inthegroupstop(),suspend(),andresume()methodsoperateinthesameway,buttheyare,ofcourse,deprecated.
Thesecondadvantageofthreadgroupsrelatestothreadsecurity.Ifyouwritecustomsecuritycodeforyourapplication,decisionsaboutwhetheronethread
canaccessand/ormodifythestateofanotherthreadtakeintoaccountthethreadgrouptowhichthethreadsbelong.TheJavaPluginandappletviewer
providesuchcustomizationsothatthreadsinoneappletarepreventedfrommodifyingthethreadsinanotherapplet.Tomakesecuritydecisionsinthisway,
however,requiresthatyouwriteacustomsecuritymanager.
voidcheckAccess(ThreadGrouptg)
Checksifthecurrentthreadisallowedtomodifythestateofthethreadgrouptg
LikeallmethodsintheSecurityManagerclass,thesemethodsthrowaSecurityExceptioniftheydeterminethatperformingtheoperationwould
violatethesecuritypolicy.Asanexample,here'saconflationofthecodethattheinterrupt()methodoftheThreadclassimplements:
publicvoidinterrupt(){
SecurityManagersm=System.getSecurityManager();
if(sm!=null)
sm.checkAccess(this);
interrupt0();
}
Thisiscanonicalbehaviorforthreadsecurity:thecheckAccess()methodiscalled,whichgeneratesaruntimeexceptionifthreadpolicyisviolatedbythe
operation.Assumingthatnoexceptionisthrown,aninternalmethodiscalledthatactuallyperformsthelogicofthemethod.
BecauseonlyonemethodintheSecurityManagerclassisusedtocheckforsecurityinformation,threadsecuritypolicyisanallornothingproposition.If
thesecuritymanagerdeterminesthataparticularthreadispreventedfrominterruptingotherthreads,thatthreadisalsopreventedfromsettingthepriorityof
otherthreads.
ThecheckAccess()methoditselflookstoseewhichthreadgroupthetargetthreadbelongsto.Ifthethreadisnotamemberoftherootthreadgroup,the
checkAccess()methodimmediatelyreturnsallthreadsareallowedtomodifythestateofallotherthreadsthatarenotmembersoftherootthreadgroup.
Otherwise,thesecuritymanagerconsultsthepolicyfortheprogram.
Javasecurityisnormallydeterminedviaaseriesofpolicyfiles,includingthefiles$JAVAHOME/lib/security/java.policyand$HOME/.java.policy.Thepolicy
filesusedbyaprogramcontainamappingbetweentheURLswheretheapplicationcodewasloadedfromandthepermissiongrantedtocodeloadedfrom
thoseparticularlocations.WhenthecheckAccess()method(oranyothermethodofthesecuritymanager)iscalled,thesecuritymanagerlooksatthe
stackofthecurrentthread:everyclassonthestackmusthavepermissiontoexecutethegivenmethod.
Forthreadaccess,codemustbegrantedoneofthesetwopermissions:
permissionjava.security.AllPermission;
permissionjava.security.RuntimePermission"thread";
WhenthecheckAccess()methodiscalledandeachmethodpresentlyonthestackhasoneofthesepermissions,nosecurityexceptionisthrown.The
securitymanagerisconsultedwheneveraprogramcallsanyofthemethodslistedinTable131.
Table131.ThreadandThreadGroupmethodsaffectedbythesecuritymanager
Threadmethods
ThreadGroupmethods
Thread()(callscheckAccess()onitsthreadgroup)
ThreadGroup()
stop()
stop()
suspend()
suspend()
resume()
resume()
interrupt()
interrupt()
setPriority()
setMaxPriority()
setDaemon()
setDaemon()
setName()
destroy()
Thestop()methodishandledsomewhatdifferently.InadditiontocallingthecheckAccess()methodofthesecuritymanager,thestop()method
alsocheckstoseeiftheclassesonthestackhavethispermission:
permissionjava.lang.RuntimePermission"stopThread";
Bydefault,thispermissionisgrantedtoallcode,andtheotherthreadpermissionsarenotgrantedtoanycode.Usersandsystemadministratorsmaychange
theirpolicyfilesatwilltoallowordisallowanyofthethreadaccess.
Bydefault,then,threadscanmodifythestateofanyotherthread(includingitself)unlessthetargetthreadbelongstotherootthreadgroup.Threadsthatarein
therootthreadgroupcannotbemodifiedunlesstheuserhassetupspecificpermissionstoallowthat.
However,thesecuritypolicyoftheJavavirtualmachineisquiteflexibleandcanbeoverridenbyapplicationsatseverallevels.Aswe'vementioned,theJava
Pluginandappletviewerprovidetheirownimplementationofthesecuritymanager.WhenthecheckAccess()methodofthatsecuritymanageriscalled,
thesecuritymanagerconsultsthethreadgroupofthecallingthread:itisallowedtoaccessormodifyitsownthreadsandthreadsinanydescendentthread
groups,butnothingelse.
FormoredetailsonhowJavasecurityworks,includinghowyoucanoverridethesecuritymanager,seeJavaSecurity(O'Reilly).
Daemon Threads
Javahastwotypesofthreads:daemonanduser.Thethreadsthatwe'velookedatsofarhaveallbeenuserthreads.Theimplicationofthesenamesisthat
daemonthreadsarethreadscreatedinternallybythevirtualmachineandthatuserthreadsarethosethatyoucreateyourself,butthisisnotthecase.Anythread
canbeadaemonthreadorauserthread.
Adaemonthreadisidenticaltoauserthreadinalmosteveryway.Theoneexceptionoccursindeterminingwhenthevirtualmachineexits.Thevirtual
machineautomaticallyexitswhenallofitsnondaemonthreadshaveexited.Daemonthreadsonlylivetoserveuserthreadsiftherearenomoreuserthreads,
thereisnothingtoserveandnoreasontocontinue.
ThecanonicaldaemonthreadinJavaisthegarbagecollectionthread(and,inrecentvirtualmachines,multiplegarbagecollectionthreads).Thegarbage
collectorrunsfromtimetotimeandfreesthoseJavaobjectsthatnolongerhavevalidreferences.Ifwedon'thaveanyotherthreadsrunning,however,there's
nothingforthegarbagecollectortodo:afterall,garbageisnotspontaneouslycreated(atleastnotinsideaJavaprogram).Soifthegarbagecollectoristhe
onlythreadleftrunningintheJavavirtualmachine,clearlythere'snomoreworkforittodo,andtheJavavirtualmachinecanexit.
ThedaemonmodeofathreadissetbycallingthesetDaemon()methodwitheithertrue(settodaemonmode)orfalse(settousermode).The
setDaemon()methodcanbecalledonlybeforethethreadhasbeenstarted.Whilethethreadisrunning,youcannotcauseauserthreadtobecomeadaemon
thread(orviceversa)attemptingtodosogeneratesanexception.Tobecompletelycorrect,anexceptionisgeneratedanytimethethreadisaliveandthe
setDaemon()methodiscalled.
Bydefault,athreadisauserthreadifitiscreatedbyauserthreaditisadaemonthreadifitiscreatedbyadaemonthread.
Figure132.Aclassloaderhierarchy
Despitethesimilarityofthishierarchytothethreadgrouphierarchy,thetwoareunrelated.Threadscanfreelyshareclassesthatareloadedinotherthreads,no
matterwhatclassloaderisusedtoloadthem.
Threadsinteractwiththeclassloaderinoneparticularcase.Eachthreadisassignedaspecificclassloaderknownasthecontextclassloader.Thisclassloaderis
retrievedwiththegetContextClassLoader()methodandsetwiththesetContextClassLoader()method.
Thecontextclassloaderisusedtoloadclasses(andresources)onlyincertainspecificcases.Developersoftenassumethatthecontextclassloadercanbeused
toaffectwhereathreadloadsthingsfrominageneralcase,butthatisnottrue.Inthegeneralcase,whenathreadrunsthecodeofclassAandcomesacrossa
referenceforclassB,itattemptstoloadthecodeforclassBfromthesameclassloaderthatloadedclassA(oroneofthatclassloader'sancestorsinthe
classloadinghierarchy).ThisapproachistakenirrespectiveofwhichthreadsorclassloaderswereoriginallyinvolvedinloadingclassA.Aclassloaderknows
onlyaboutitsancestors,notitsdescendants.
Thecontextclassloaderonlycomesintoplaywithcertaininternalclassesinthevirtualmachine.Forexample,whenyoupassserializedobjectsoverIIOP,the
ORBclassesconsultthethread'scontextclassloaderwhenittriestoretrievetheclassdefinitionfortheclassesitattemptstodeserialize.Applicationservers
typicallytakethesameapproachwhenattemptingtoloadresourcesspecifictoaJ2EEapplication.
ThereasonacontextclassloaderisneededinthesecircumstancesisthattheORBclasseswereloadedbythesystemclassloadertheydon'tknowaboutany
otherclassloadersthatexistintheclassloadinghierarchy.WhenanORBclassdereferencesanobjectandneedstoloadanewclass,itcanconsultonlythe
systemclassloader.Clearly,theapplicationclassesitneedstodeserializetheobjectwon'tbedefinedinthesystemclassloaderitmusthaveahooktogettothe
specialclassloaderthattheapplicationwantstousetodefineitsclasses.
Thishookisunrelatedtothreadingissues:thecontextclassloadercanbesetandresetasoftenasyouwantinyourapplication.TheThreadclasssimply
providesaconvenientlocationtoputthishook.
Thedefaultcontextclassloaderforathreadistheclassloaderthatloadedtheclassdefiningthethread.Forapplicationthreads,thisistypicallytheapplication
classloader(unlessyou'vedefinedyourownclassloaderwithintheapplication).Sointhevastmajorityofcases,youdon'tneedtoworryaboutsettingthe
contextclassloader.Ifyou'vedefinedandusedmultipleclassloadersinyourapplication,however,youneedtosetthecontextclassloaderofathreadbeforeit
callsintotheORB(orcertainothersystemresources).
method.Let'sexaminethatinalittlemoredetail.Thestart()methoddoesstartanotherthreadofcontrol,buttherun()methodisnotreallythe"main"
methodofthenewthread.Therun()methodisexecutedinsideacontextthatallowsthevirtualmachinetohandleruntimeexceptionsthrownfromthe
run()method.ThisprocessisshowninFigure133.
Figure133.Flowchartofthemainthread
Alluncaughtexceptionsarehandledbycodeoutsideoftherun()methodbeforethethreadterminates.ThedefaultexceptionhandlerisaJavamethoditcan
beoverridden.Thismeansthatitispossibleforaprogramtowriteanewdefaultexceptionhandler.
ThedefaultexceptionhandleristheuncaughtException()methodoftheThreadGroupclass.Itiscalledonlywhenanexceptionisthrownfromthe
run()method.Thethreadistechnicallycompletedwhentherun()methodreturns,eventhoughtheexceptionhandlerisstillrunningthethread.
ThedefaultimplementationoftheuncaughtException()methodistoprintoutthestacktraceoftheThrowableobjectthrownbytherun()method
(unlessthatobjectisaninstanceoftheThreadDeathclass,discussednext).Inmostcases,thisissufficient:theonlyexceptionsthattherun()methodcan
throwareruntimeexceptionsorerrors.Bythetimetherun()methodhasreturned,it'stoolatetorecoverfromtheseerrors.
Onecaseinwhichit'susefultooverridetheuncaughtException()methodistosendaprioritynotificationtoanadministratorthatanunusual,fatalerror
hasoccurred.Here'sanexamplethatdoesthatwhenitsthreadeventuallyencountersanoutofmemoryerror:
packagejavathreads.examples.ch13;
importjava.util.*;
publicclassTestOverrideimplementsRunnable{
staticclassOverrideThreadGroupextendsThreadGroup{
publicOverrideThreadGroup(){
super("AdministratorAlertGroup");
}
publicvoiduncaughtException(Threadt,Throwablee){
alertAdministrator(e);
}
}
publicstaticvoidalertAdministrator(Throwablee){
//UseJavaMailtosendtheadministrator'spageranemail
System.out.println("Adminstratoralert!");
e.printStackTrace();
}
publicstaticvoidmain(String[]args){
ThreadGrouptg=newOverrideThreadGroup();
Threadt=newThread(tg,newTestOverride());
t.start();
}
publicvoidrun(){
ArrayListal=newArrayList();
while(true){
al.add(newbyte[1024]);
}
}
}
Whentheoutofmemoryerroroccurs,theapplicationprintsamessagealertingthesystemadministratorofthisfact.InJ2SE5.0,thisideahasbeen
expanded,anditisnowpossibletosetanuncaughtexceptionhandlerforeachthread.
packagejava.lang;
publicclassThreadimplementsRunnable{
publicinterfaceUncaughtExceptionHandler{
voiduncaughtException(Threadt,Throwablee);
}
publicstaticsetDefaultExceptionHandler(Thread.UncaughtExceptionHandlerueh);
publicstaticThread.UncaughtExceptionHandlergetDefaultExceptionHandler();
publicsetExceptionHandler(Thread.UncaughtExceptionHandlerueh);
publicThread.UncaughtExceptionHandlergetExceptionHandler();
}
ThestaticmethodsoftheThreadclasssetorretrieveadefaultthreadhandlerusedbyallnewthreads.Whenathreadisconstructed,itsexceptionhandleris
settothedefault,socallingthesetDefaultExceptionHandler()methoddoesnotaffectanythreadsthathavealreadybeenconstructed.Theexception
handlerforaparticularthreadcanbesetatanytime.
Bydefault,theexceptionhandlerforathreadisitsthreadgroup:theThreadGroupclassimplementstheThread.UncaughtExceptionHandler
interfaceandcallstheuncaughtException()method,aswe'vealreadyexplained.Therefore,thechangestoJ2SE5.0arefullybackwardcompatiblewith
existingexceptionhandling.
packagejavathreads.examples.ch10;
importjava.util.*;
importjava.text.*;
publicclassTaskimplementsRunnable{
longn;
Stringid;
privatelongfib(longn){
if(n==0)
return0L;
if(n==1)
return1L;
returnfib(n1)+fib(n2);
}
publicTask(longn,Stringid){
this.n=n;
this.id=id;
}
publicvoidrun(){
Dated=newDate();
DateFormatdf=newSimpleDateFormat("HH:mm:ss:SSS");
longstartTime=System.currentTimeMillis();
d.setTime(startTime);
System.out.println("Startingtask"+id+"at"+df.format(d));
fib(n);
longendTime=System.currentTimeMillis();
d.setTime(endTime);
System.out.println("Endingtask"+id+"at"+
df.format(d)+"after"+(endTimestartTime)+
"milliseconds");
}
}
Whenathreadexecutestherun()methodofthisclass,itcreatesastackframe.Thestackframehasinformationspecifictothisexecutionoftherun()
method.Thatmeansithasaplacetostorethelocalvariables(d,df,startTime,andendTime)thatthemethodexecutionuses.Whentherun()method
callsthefib()method,anewstackframerepresentingthefib()methodisplacedonthestack.Thatstackframehasstorageforthelocalvariable(n)of
thefib()method.Successivecallstothefib()methodplaceanewframeonthestack,eachwithitsownlocalcopyofthevariablen.Atsomepoint,
then,thestackresemblesFigure134:therun()methodhascalledthefib()methodwithavalueof2,andthefib()methodhasrecursivelycalleditself.
Atthispoint,asthefib()methodreturns,framesarepoppedoffthestack,freeingmemoryforlateruse.
Figure134.AJavathread'sstack
Stackframescontainmoreinformationthanthelocalvariablesofamethod:theycontainprogramcountersthatindicatewhichstatementinthemethodthe
threadisexecutingandotherbookkeepinginformationforthethread.Thesizeofthelocalvariablesplusthesizeofthisbookkeepinginformationdetermines
thesizeofthestackframe.
Thatsizeisplatformdependent.Althoughthelocalvariablesmusthavethesamesize(sinceJavadefinesthesizeofallvariables),thespaceneededtostore
thelocalvariablesmaydifferacrossplatform.Forexample,certainCPUsworkbetterifvariablesarealignedonanevenwordboundaryoran8byte
boundary.Therefore,astackframethatdefinesfourseparatevariablesoftypebytemaybeabletostorethosevariablesin4bytesonsomeCPUsbut
require16ormorebytesonotherCPUs.Inaddition,thebookkeepinginformationforastackisdependentontheJavaimplementationitmayvarybetween
differentJavareleasesonthesameplatform(aswellasdifferingbetweenplatforms).
Thesizeofthestack(andtheframesitholds)impactsJava'smemoryusageintwoways:stackoverflowsoccurwhenastackisnotbigenough,andoutof
memoryconditionscanoccurwhenstacksaretoobig.
example,thefollowingcommandwouldmakeallJavathreadsuseastackof128KB:
%javaXss128kMyClass
Thisisagoodtechniquetousewhenyourapplicationthrowsanoutofmemoryerrorwhencreatinganewthread,sinceitdoesn'trequirechangingyour
programcode.However,thisargumentisnonstandard,soitisnotavailableinallJavaimplementations.
Stack APIs
TheThreadclasshasfourmiscellaneousmethodsthatprovideinformationaboutaJavastack:
packagejava.lang;
publicclassThreadimplementsRunnable{
publicintcountStackFrames();//deprecated
publicstaticvoiddumpStack();
publicStackTraceElement[]getStackTrace();
publicstaticMapgetAllStackTraces();
}
ThecountStackFrames()methodcanonlybecalledonathreadthatissuspendeditsuseisdeprecatedsincethesuspend()methodisdeprecated.In
J2SE5.0,youcanusethegetStackTrace()methodinstead,whichreturnsallthemethodsonthestackofthetargetthread.The
getAllStackTraces()methodcallsthegetStackTrace()methodforeverythreadinthevirtualmachineandreturnsamapcontainingthestack
tracesforallthethreads.ThedumpStack()methodisautilitymethodtodumpthecurrentthread'sstacktoSystem.err.
Summary
Inthischapter,we'vefilledinalotofthedetailsabouthowthreadswork.Threadsbelongtoathreadgroup,andthreadgroupsexistinahierarchicalformat.
Threadgroupsserveafewpurposes:theyallowyoutointerruptagroupofthreadswithonemethodcall,andtheyallowacustomsecuritymanagertomake
surethatunrelatedthreadscannotinterferewitheachother.
We'vealsolookedathowthreadshandleuncaughtexceptions:thoughtheynormallyjustprintouttheuncaughtexceptiontoSystem.errandexit,youcan
arrangefortheexitingthreadtoperformonefinalact.Finally,we'veseenhowthreadsinteractwiththeJavaheapandmemorysystemsandhowyoumay
needtoadjustmemoryparametersinaprogramthathandlesalotofthreads.
Example Classes
HerearetheclassnamesandAnttargetsfortheexamplesinthischapter:
Description
MainJavaclass
Anttarget
UncaughtExceptionhandlertest
javathreads.examples.ch13.TestOverride
ch13ex1
[ 1 ]
NotallvirtualmachinelevelthreadshaveacorrespondingJavathreadobject,sothesystemgroupdoesnotcontainallpossible
threads.
Chapter14.Thread Performance
Inafewplacesinthisbook,we'vereferredtoperformancecharacteristicsofthreadrelatedprogramming.We'veglossedoveralotofthatinformationinthis
chapter,we'lllookattheseperformanceissuesinmoredepth.Inparticular,we'lllookatthreadcreationperformance,theperformanceadvantagesofusinga
threadpool,andtherealcostsofsynchronization.However,we'llstartwithanoverviewoffactorsthataffectJavaperformance.
Overview of Performance
Mostdevelopersareconcernedabouttheperformanceoftheirprogram.Eventhoughtherearemanyprogramsforwhichperformancedoesn'treallymatter,
noonewantstowriteabadlyperformingprogram.Andtherearemanymoreprogramsforwhichperformanceiscrucial.
Performance,however,isnotthemostimportantaspectindevelopinggoodprograms.We'vefrequentlymetdeveloperswhoallowtheirconcernsabout
performancetocomplicatetheirprogramdevelopment:forexample,believingthatsynchronizationisinherentlyexpensive,theymayspenddaysattempting
towriteaclassthatdoesn'tneedsynchronization.Theresultingcodeiscomplex,difficulttomaintain,andmorepronetobugsthanasimpler(inthiscase,
synchronized)version.
Withoutanypriorknowledgeofaprogram'sbehavior,thisiscounterproductive.Developertimeiswasted,andsupportcostsareincreased.Thisobservation
[ 1 ]
leadsustoourfirstruleofperformance:prematureoptimizationistherootofmuchevil.
Performancebottleneckscanbeassessedonlythroughactualobservation.Theriskindoingotherwiseisthatyoumayspendalotoftimetryingtomakecode
runfasterwithnomeasurableeffectonyourprogramwhileinthemeantimeignoringtheprogram'sactualperformancebottlenecks.Consequently,our
secondruleofperformance:
Makeperformancetestingaregularpartofthedevelopmentcycle.
Inanidealsituation,codingwouldgothroughacycleshowninFigure141.Regularprofilingoftheapplicationisolatesthoseareasofthecodethatneed
optimization,increasingtheproductivityofdevelopers.
Figure141.Theperformanceawaredevelopmentcycle
majorloopsofyourapplicationafewthousandtimestomakesurethatthecodeiswelloptimizedbeforetimingit.
AsecondcomplicationisintroducedbyJava'sgarbagecollector.Onceagain,ifyou'retiminganactualtask,theeffectofthegarbagecollectordoesn'tmatter:
thegarbagecollectortakeswhatevertimeittakesasthetaskruns.Butifyou'retryingtowriteamicrobenchmarkandtimediscreteoperations,anytimethe
garbagecollectorruns,itwillthrowoffyourtiming.
Inourtests(availableintheonlinesource),weexecutecertainmethodsalargenumberoftimes.Wewarmupthecompilerbyexecutingthemethod10,000
timesandthentimeexecutingthemethodforalargenumberofiterations.Thisreducestheeffectofcompilationonthetest.WealsocalltheSystem.gc()
andSystem.runFinalization()methodsinbetweenmeasurements,whichlimitstheeffectofgarbagecollectiontothatwhichisdirectlyattributableto
themethodbeingexecuted.
ManyplatformspecificfactorsaffecttheperformanceofJavaprograms.Differentoperatingsystemsshowdifferentperformanceforthreadcreationand
synchronization.OptimizationswithintheJavavirtualmachineitselfmeanthatdifferentvirtualmachineimplementationsshowdifferentbehavior.Faster
chipsaffecttimingsaswell.Allofthisistosaythattheconclusionswedrawherearebasedonthedatathatweproducedonthegivenplatforms,butyour
mileagemayvary.Thisisyetanotherreasonwhyperformingyourownmeasurementsissoimportant.
ThetestsreportedinthischapterwererunwiththebetaversionofJ2SE5.0onmachineswiththefollowingCPUsandmemory:
CPUs
Javavirtualmachinearguments
Operatingsystem
SunMicrosystemsUltraSPARCIIIFourCPUs,750MHz
serverXms3500mXmx3500m
Solaris9OperatingEnvironment
IntelXeonTwoCPUs,1400MHz
serverXmn1800mXms1800m
RedHatLinuxAdvancedServer3.0
IntelXeonTwoCPUs,3060MHz
serverXmn1600mXms1600m
MicrosoftWindowsServer2003
Differencesinheapsizereflecttheunderlyingsupportoftheoperatingsystemforthelargestheappossible(whichhelpswiththegarbagecollectionissue).
Whilewequotenumbersforspecifictests,it'sbesttoconsiderthemintermsoforderofmagnitudeapproximations(ratherthan,forexample,concluding
thatittakesexactly184.5nanosecondstoperformasynchronizationoperationonanUltraSPARCIIICPU).
Synchronized Collections
Let'slookintosomesynchronizationissues,startingwithaquestion.Whenshouldyouuseanunsynchronizedcollectionclass?We'regoingtoarguethatthe
timeswhenyouneedtodothatareveryrareindeed.
Toreachthisconclusion,welookedattheperformanceofaddingobjectstofourkindsoflists:vectors,arraylists,synchronizedarraylists,andamodified
vectorclassfromwhichweremovedsynchronization.Specifically,we'retestingthismethod:
publicvoiddoTest(Listl){
Integern=newInteger(0);
for(inti=0;i<nLoops;i++)
l.add(n);
}
ForasufficientlylargevalueofnLoops,takingthetimetoexecutethismethodwhenthelistissynchronized,subtractingthetimerequiredtoexecutethe
methodwhenthelistisunsynchronized,anddividingbynLoopsgivesusafairapproximationofthetimerequiredtosynchronizedtheadd()method(and
ingeneral,toobtainanuncontendedsynchronizationlock).
AlthoughtheVectorandArrayListclassesareconceptuallysimilar,theirimplementationdiffersenoughthattheyarenotcomparableforthistest.
Therefore,wecomparetheVectorclasstoamodifiedversionofthatclass,andwecomparetheArrayListclasstotheclassreturnedfromthe
Collections.synchronizedCollection()methodwhengivenanarrayclass.Inbothcases,theaveragetimedifferenceisaboutthesameandis
showninTable141.
Table141.Timedifferenceofsynchronizedversusunsynchronizedmethodinvocations
Testplatform
Synchronizedversusunsynchronizedmethods:timedifferencepermethodinvocation
SPARC/Solaris
185nanoseconds
Intel/Linux
65nanoseconds
Intel/Windows
92nanoseconds
Thisisasinglethreadedtest,ofcourse,sinceaccesstoanarraylist(orourmodifiedvectorclass)isnotthreadsafe.Soaccesstothesynchronizedmethodsof
theVectorclassisalwaysuncontended.Modernvirtualmachines(startingwithSun'sHotSpotimplementationforJDK1.2,andimprovingafterthat)are
writtensothatuncontendedlockacquisitionisveryfastindeed:dependingonthespeedoftheunderlyingCPU,aslittleas65nanoseconds.
Theperformanceofcontendedlocksismuchdifferent(aswe'llseeinthenextsection).Butifyou'replanningtouseanunsynchronizedcollectionclass,
accesstothesynchronizationlockisnecessarilyuncontended.
Ifyoureallyknowthataparticulardatastructurewon'tbeaccessedbymorethanonethread,youcansaveafewnanosecondsanduseanunsynchronized
one.Butingeneral,there'snorealpenaltyforusingasynchronizedcollectionclass,anddoingsocanoftenpreventinadvertentraceconditionsfromplaguing
yourprogram.Forthatreason,weprefertousesynchronizedcollectionsalmostallthetime,whichallowsourclassestobe(re)usedinanyprogram.
Timedifferencebetweenatomicvariablesandsynchronizedmethods
Testplatform
Onethread
Twothreads
Eightthreads
SPARC/Solaris
92nanoseconds
1400nanoseconds
650nanoseconds
Intel/Linux
20nanoseconds
700nanoseconds
400nanoseconds
Intel/Windows
60nanoseconds
3200nanoseconds
5800nanoseconds
Whenthereisonlyonethreadrunning,thelocksareuncontended,andwegetsimilarresultsasourlastexample:thereisavery,veryslightbenefittousing
anatomicvariable.Whentherearetwothreads,contentionforthelockisintroduced.Nowthedifferencebecomesmuchgreater:asmuchasthree
microseconds.That'sasignificantdifferenceformanyprograms.
Muchmoreinterestingiswhathappenswhenmanythreadsarecontendingforthelock(ortheatomicvariable).Nowthedifferencehasbeencutinhalfon
Unixsystems.Thisisbecausetheatomicvariablemethodsloopuntiltheyachievethedesiredresult(aswesawinourexamplesinChapter5).
It'salsointerestingtonotethatthisbehaviorisnotobservedonWindowsServer2003.That'smoreareflectionofthegreatlyincreasedcostofthecontention
forthesynchronizedlockonthisplatform.Witheightthreadscontendingforthesinglelock,theincreaseintheoperatingsystemtimetoservicethelock
contentionismuchgreaterthantheincreaseintimespentwithintheloopswithintheclassusingatomicvariables.Inbothtests,theWindowsServer2003
platformspendsagreatdealmoretimedealingwitheightthreadsthantwothreads,buttheproportionoftimefavorsusingatomicvariables.
Ifprofilesofyourprogramshowagreatdealoftimespentwaitingforparticularlocks,refactoringthecodetouseatomicvariablesis,ifpossible,agood
solutionforremovingthatbottleneck.ButaswesawinChapter5,writingaclasstousemultipleatomicvariablescanbecomplexitmaynotbeworththe
effortunlessyouknowthatyou'refacingaperformancebottleneck.
TimedifferencebetweenConcurrentHashMapandHashtable
Testplatform
Onethread
Twothreads
Eightthreads
SPARC/Solaris
100nanoseconds
3500nanoseconds
2000nanoseconds
Intel/Linux
200nanoseconds
1500nanoseconds
1100nanoseconds
Intel/Windows
25nanoseconds
6000nanoseconds
13,000nanoseconds
Intheuncontendedcase(whereyoucoulduseaHashMapratherthanaHashtable,thoughwetestonlythelattercase),thereislittletonodifference
betweenimplementations.Infact,asCPUsgetfaster,thesimplerimplementationoftheHashtableclassallowsittoperformslightlybetter.
Asweaddcontention,theHashtableclasspaysthepredictablepenalty,andnowtheconcurrenthashmapisatleast1.5microsecondsfaster(andmoreon
otherplatforms).Becauseofthe"optimistic"natureoftheconcurrenthashmap,theadvantageismitigatedsomewhatasweaddmorecontention(excepton
WindowsServer2003again,wheretheaddedlockcontentionintheoperatingsystemoverwhelmstheaddedcodeexecutedbytheconcurrenthashmap).
Testplatform
Timedifferencebetweenthreadcreationandthreadpool
SPARC/Solaris
400microseconds
Intel/Linux
175microseconds
Intel/Windows
190microseconds
Afewhundredmicrosecondsisnothingtosneezeatincomputertime.Inanapplicationserver,youmightreasonablyexpectaquickanswerfromtheserver:
maybesomethinginafewmicroseconds.Startingathreadforthoserequestswouldindeedcauseaprofounddifferenceintheapplicationresponsetime.
Inmanyprogramsthisadditionaloverheaddoesnotmakeabigdifference.OnourSolarisplatform,thistesttook38.5secondstorunandcreated100,000
threadscomparedto.1secondstorunwithathreadpool:almost400timeslonger.
Ontheotherhand,ourprogramdoesn'tdoanythinginterestingatall.Ifthelogicofourtargetmethodtook20milliseconds,creatingthreadsforthetasks
wouldtakeonly2%longer.Atsomepoint,theaddedtimetocreatethethreadsbecomeslostintheactualcalculationtime.
Themoralofthestoryisifyouneedtospawnafewthreads,don'tsweatit.Ifyoucreatealotofthreads,lookatyourprogramprofilesandresponsetimesto
seeifathreadpoolmakessense.Butoverall,usewhatmakessenseforyourprogramdesign.
Summary
Performanceisanoverridingconcernformanydevelopers,andperformanceofthreadrelatedconstructsoccupiesaprominentpositioninthemindofthe
performanceorientedJavadeveloper.Inthischapter,weexaminedthebasicperformanceofsimplethreadconstructs:threadcreationandsynchronization.
Wefoundthatthreadcreationischeapenoughsothatitdoesn'tmatterinmanycases,thatthere'snoreasontouseanunsynchronizedcollectioninsteadofa
synchronizedone,andthatcontendedlockscanbecomeveryexpensive.Thelattercasecansometimesbeavoidedbyusingatomicvariablesfordataaccess.
It'simportanttomeasureyourparticularprogramtoseeiftheseissuesaffectit.Adevelopmentcyclethatincludesfrequentperformancemeasurementscan
helpyounarrowdowntheperformancebottlenecksofyourprogramandfocusyoureffortsonthemoreimportantspotsoftheprogram.
Example Classes
TheonlineexampleshaveourtestcodeandcanberunwiththefollowingclassesorAnttargets:
Description
MainJavaclass
Anttarget
SynchronizedCollectionTest
javathreads.examples.ch14.CollectionTestnLoops
ch14ex1
AtomicTest
javathreads.examples.ch14.AtomicTestnLoopsnThreads
ch14ex2
HashtableTest
javathreads.examples.ch14.HashTestnLoopsnThreads
ch14ex3
ThreadCreationTest
javathreads.examples.ch14.CreateTestnLoops
ch14ex4
TheAnttargetsacceptthefollowingproperties:
<propertyname="nLoops"value="100000"/>
<propertyname="nThreads"value="10"/>
[ 1 ]
TonyHoareiscreditedwithoriginatingthequote"Prematureoptimizationistherootofallevil,"andDonaldKnuthhaswidely
popularizedthatsaying.We'renotpreparedtogoquitethatfar.
packagejavathreads.examples.ch15.example1;
publicclassSinTable{
privatefloatlookupValues[]=null;
publicsynchronizedfloat[]getValues(){
if(lookupValues==null){
lookupValues=newfloat[360*100];
for(inti=0;i<(360*100);i++){
floatsinValue=(float)Math.sin(
(i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
}
}
returnlookupValues;
}
}
Thiscodeisthebasisofourexamplesintherestofthischapter.Asinglethread,andthereforeasingleprocessor,executestheloopasspecifiedinthecode
andstorestheresultsinthelookupValuesarray.AssumingthatthecalculationofthesinValuevariableistimeconsuming,thewholeloopmaytakea
longtimetoexecute.Insomecases,thisisacceptable.However,ona12processorcomputerwithoutanyotherprogramsrunning,onlyoneCPUisworking
whiletheother11aresittingidle.Consideringthecostofa12processormachine,thisisnotacceptable.
Beforewegetstarted,let'sdefinesometerminology.ThevariablesinValuehasafewspecialproperties.Obviously,itexistsonlyforthedurationofthe
loop.Itisatemporaryvariableusedtoaidthecalculationofthelookuptable.Itdoesnotcarryavalueinoneiterationoftheloopthatisusedinanother
iterationoftheloop,andthevalueofthevariableisreassignedinthenextiteration.WedefinesinValueasaloopprivatevariable,thatis,avariablethatis
initialized,calculated,andusedentirelyinasingleiterationoftheloop.
Furthermore,wecanstatethattheindexvariableiisalsoaloopprivatevariable:itisalsousedcompletelyinaniterationoftheloop.Itcanbeconsidereda
specialtypeofloopprivatevariable.Sinceitisneverchangedduringaniterationandisdirectlytiedtotheiterationindex,wecanactuallytreatitasaconstant
duringtheiterationofaloop.However,fornow,simplyconsideringitasaloopprivatevariableisgoodenough.
Wemaytrytobreakthepartsofthisloopamongmanythreadsasfollows:
packagejavathreads.examples.ch15.example2;
publicclassSinTableimplementsRunnable{
privateclassSinTableRange{
publicintstart,end;
}
privatefloatlookupValues[];
privateThreadlookupThreads[];
privateintstartLoop,endLoop,curLoop,numThreads;
publicSinTable(){
lookupValues=newfloat[360*100];
lookupThreads=newThread[12];
startLoop=curLoop=0;
endLoop=(360*100);
numThreads=12;
}
privatesynchronizedSinTableRangeloopGetRange(){
if(curLoop>=endLoop)
returnnull;
SinTableRangeret=newSinTableRange();
ret.start=curLoop;
curLoop+=(endLoopstartLoop)/numThreads+1;
ret.end=(curLoop<endLoop)?curLoop:endLoop;
returnret;
}
privatevoidloopDoRange(intstart,intend){
for(inti=start;i<end;i+=1){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
}
}
publicvoidrun(){
SinTableRangestr;
while((str=loopGetRange())!=null){
loopDoRange(str.start,str.end);
}
}
publicfloat[]getValues(){
for(inti=0;i<numThreads;i++){
lookupThreads[i]=newThread(this);
lookupThreads[i].start();
}
for(inti=0;i<numThreads;i++){
try{
lookupThreads[i].join();
}catch(InterruptedExceptioniex){}
}
returnlookupValues;
}
}
Thecodeinthisnewversionisfunctionallythesameasthepreviousversion,albeitwithmanymodificationstoitslogic.First,insteadofaloopthatdoesthe
calculation,wenowhavealoopthatstartsoff12(numThreads)differentworkerthreadsandprovideseachworkerthreadwithdifferentpartsofthe
mathematicallooptocalculate.Theoriginalmathematicalcalculationismovedtoanewmethod,loopDoRange().Inthismethod,theloophasbeen
modifiedtoworkononlypartofthelookuptableinsteadofthewholetable.Eachdifferentthreadisresponsibleforcalculatingonlyitsportionofthetable.
EachthreadmustcalltheloopGetRange()methodtodeterminewhichportionitmustcalculate.Thethreadthatstartedthe12workerthreadsthensimply
waitsforall12workerthreadstofinish.Sincethelongcalculationisnowaccomplishedby12threadsinsteadofbyasinglethread,itisnowpossiblefora
multiprocessorbasedoperatingsystemtoplacethedifferentthreadsondifferentprocessors.
Thecalculationworksforanumberofreasons.First,theloopindexvariableiandthesinValuevariable,whichwereoriginallyclassifiedasloopprivate,
arenowstackvariablesineachworkerthread.TheloopDoRange()methodusesdifferentcopiesofthesetwovariablesineachthreadexecutingtheloop.
Thismeansthateachofthe12workerthreadshasitsowncopyofthesevariableswhilecompletingitsportionofthecalculation.
Second,althoughthelookupTablearrayisnotloopprivate,theindividualmembersofthearraycanbeconsideredloopprivate.Eachindividualmember
ofthearrayisaccessedonlyinaparticulariteration.Thereisnoraceconditionbecauseeachiterationaffectsoneandonlyonememberofthearray,and
althoughthedifferentworkerthreadshandlemanyiterationsoftheloop,nosingleiterationishandledbymorethanonethread.
Theonlysynchronizationweneedisintheassignmentofthedifferentranges.Topreventtheworkerthreadsfromsteppingoneachotherduringthis
assignment,theloopGetRange()methodissynchronized.Inthisexample,sincetheloopispartitionedintoonly12ranges,thereislittlecontentionfor
thislock.
Thecodeforthisnewversionismorecomplicatedthanourfirstversion.Thisnewcodenowhastostartandtrack12separatethreads.Theworkerthreads
hadtobemodifiedtohandlepartsoftheloopwhoserangestheyhavetodetermine.Althoughverylittlesynchronizationisneededinthiscase,wecould
easilyhavehadacomplicatedrequirementforsynchronizationdependingonthealgorithmusedinthemathematicalcalculation.
Giventhecomplexityweintroducedtohandlethissimpleloop,itmaybecometoohardtohandlemorecomplexloops.Tohelpwiththiscomplexity,we'll
moveallthelogicrelatedtoloopmanagementintoaseparateclass.Wecanthenimplementtheloopbysimplyusingtheservicesprovidedbythisclass:
packagejavathreads.examples.ch15;
publicclassLoopHandlerimplementsRunnable{
protectedclassLoopRange{
publicintstart,end;
}
protectedThreadlookupThreads[];
protectedintstartLoop,endLoop,curLoop,numThreads;
publicLoopHandler(intstart,intend,intthreads){
startLoop=curLoop=start;
endLoop=end;
numThreads=threads;
lookupThreads=newThread[numThreads];
}
protectedsynchronizedLoopRangeloopGetRange(){
if(curLoop>=endLoop)
returnnull;
LoopRangeret=newLoopRange();
ret.start=curLoop;
curLoop+=(endLoopstartLoop)/numThreads+1;
ret.end=(curLoop<endLoop)?curLoop:endLoop;
returnret;
}
publicvoidloopDoRange(intstart,intend){
}
publicvoidloopProcess(){
for(inti=0;i<numThreads;i++){
lookupThreads[i]=newThread(this);
lookupThreads[i].start();
}
for(inti=0;i<numThreads;i++){
try{
lookupThreads[i].join();
lookupThreads[i]=null;
}catch(InterruptedExceptioniex){}
}
}
publicvoidrun(){
LoopRangestr;
while((str=loopGetRange())!=null){
loopDoRange(str.start,str.end);
}
}
}
InournewLoopHandlerclass,wehaveimplementedthelogicthatweappliedinourSinTableclass.Thelogicofcreating,tracking,andjoiningback
withtheoriginalthreadhasbeenmovedtothenewlycreatedloopProcess()method.Thelogicofdeterminingtherangesandprocessingtheloop
originallycodedintherun()andloopGetRange()methodsoftheSinTableclassremainsnearlyunchanged.Theloophandlerhasalsobeen
modifiedtohandlemoregenericloopsandhasaconstructorthatassignsthestartoftheloop,theendoftheloop,andthenumberofthreads.Justasinour
earlierexample,thealgorithmcallstheloopDoRange()methodtohandletheprocessing.However,inthiscase,theLoopHandlerclasshasanempty
implementationforthismethod.
NowourimplementationoftheSinTableclassismuchsimpler:
packagejavathreads.examples.ch15.example3;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsLoopHandler{
privatefloatlookupValues[];
publicSinTable(){
super(0,360*100,12);
lookupValues=newfloat[360*100];
publicvoidloopDoRange(intstart,intend){
for(inti=start;i<end;i++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
}
}
publicfloat[]getValues(){
loopProcess();
returnlookupValues;
}
}
Inthiscase,wesimplyconfiguretherangesneededbytheloophandler,providethelogicoftheloopintheloopDoRange()method,andcallthe
loopProcess()methodtoprocesstheloopinamultithreadedfashion.WhilethisisstillmorecomplicatedthanthefirstSinTableclassimplementation,
itisnowmuchmoremanageableandlesscomplexthanthepreviousimplementation.
SELF-SCHEDULING
Inselfscheduling,eachworkerthreadgrabsasmallchunkoftheiterationstoexecute.Aftercompletionofitsassignedrange,itgrabsanothersmallchunk.If
1000loopiterationsaretobedistributedand10threadsareassignedtothetask,eachworkerthreadworksonasmallchunke.g.,20untilall1000
iterationsarecompleted.
Aswithstaticscheduling,thedifferentworkerthreadsmaynotcompleteatthesametime.However,sincethechunksaresmallintheselfschedulingmodel,
theidletimeofthethreadsattheendoftheprocessisalsosmall.Tomakethisidletimeevensmaller,wecanmaketheindividualchunkssmaller.However,
thereisanoverheadinobtainingtherangestoexecutethisoverheadincreasesasthechunksgetsmaller.
Here'sanimplementationofthismodel:
packagejavathreads.examples.ch15;
publicclassSelfLoopHandlerextendsPoolLoopHandler{
protectedintgroupSize;
publicSelfLoopHandler(intstart,intend,intsize,intthreads){
super(start,end,threads);
groupSize=size;
}
protectedsynchronizedLoopRangeloopGetRange(){
if(curLoop>=endLoop)
returnnull;
LoopRangeret=newLoopRange();
ret.start=curLoop;
curLoop+=groupSize;
ret.end=(curLoop<endLoop)?curLoop:endLoop;
returnret;
}
}
Implementationofaselfschedulingloophandlerisstraightforward.OurcurrentLoopHandlerclassalreadyhasthelogicofworkinguntiltheloop
completes.WesimplyneedtomodifytheconstructortohandlethechunksizerequestedandmodifytheloopGetRange()methodtoreturnthisfixed
[ 1 ]
chunksize.Inourimplementationoftheselfscheduler,wesimplysubclassfromtheoriginalloophandlerandimplementonlythechanges.
GUIDED SELF-SCHEDULING
Guidedselfschedulingisacompromisebetweenthestaticschedulerandtheselfscheduler.Inthebeginning,theguidedschedulergrabsalargenumberof
iterationsoftheloop,whichbecomesprogressivelysmallerneartheendoftheloop.Theguidedselfscheduleralsousesaminimumchunksize.Thus,it
basicallybehaveslikeastaticschedulerthatslowlybecomesaselfscheduler.
If1000iterationsinthelooparetobedistributedand10threadsareassignedtothetask,thefirstworkerthreadgetsonetenthofthework100iterations.
Thesecondthreadgetsonetenthoftheremainingwork90iterations.Thisslowlygetssmallerandsmalleruntiltheminimume.g.,10isassignedthe
minimumisassigneduntilall1000iterationsarecompleted.
Thisalgorithmseemstohavethefewestproblems.Unliketheselfscheduler,theextraoverheadappearsonlyattheendoftheloop.Andunlesstheindividual
iterationshavedrasticallydifferentexecutionperiodsfromthelongertermiterationsatthebeginning,itdoesn'thavetheproblemsthatthestaticschedulerhas.
Here'showtoimplementguidedselfscheduling:
packagejavathreads.examples.ch15;
publicclassGuidedLoopHandlerextendsPoolLoopHandler{
protectedintminSize;
publicGuidedLoopHandler(intstart,intend,intmin,intthreads){
super(start,end,threads);
minSize=min;
}
protectedsynchronizedLoopRangeloopGetRange(){
if(curLoop>=endLoop)
returnnull;
LoopRangeret=newLoopRange();
ret.start=curLoop;
intsizeLoop=(endLoopcurLoop)/numThreads;
curLoop+=(sizeLoop>minSize)?sizeLoop:minSize;
ret.end=(curLoop<endLoop)?curLoop:endLoop;
returnret;
}
}
Implementationofaguidedselfschedulingloophandlerisalsostraightforward.Wesimplyneedtomodifytheconstructortohandletheminimumsize
required,andmodifytheloopGetRange()methodtoreturnaportionoftheremainingloop.Inourimplementationoftheguidedselfscheduler,wealso
subclasstheoriginalloophandlerandimplementonlythechanges.
USER-DEFINED SCHEDULING
Theimplementationoftheselfschedulerandtheguidedselfschedulerissimpleforareason:itwasdesignedtobeso.Theoriginalloophandlerwas
designedtobesubclassedsothatthescheduleralgorithmcouldbemodified.Asgoodastheimplementationoftheguidedselfschedulermaybe,itisstill
designedforagenericloop.Insomecases,oneschedulerworksbetterthananother.However,ifenoughinformationconcerningtheloopisknownandthe
effortislargeenough,itmayjustifytheimplementationofyetanotherscheduler.Thisentailsfiguringouttheappropriatelogicandcodinganew
loopGetRange()method.
TouseanyoftheseotheralgorithmsinourSinTableclass,wesimplysubclassfromtheappropriatehandlerclassandmodifyourconstructortopassthe
minimumchunksize.
Variable Classifications
IntheimplementationoftheSinTableclass,weclassifythevariablesusedintheoriginalunthreadedloopasloopprivatevariables,butothervariable
classificationsexist.Thereasonforclassifyingvariablesatallisthatdifferenttypesofvariablesrequiredifferenttypesofhandlingwithinandbetween
threads.Manyloopshaveadatadependencythatoccursbetweeniterations.Byclassifyingthevariables,weareabletocorrectlyupdateandmodifythem
withoutanyraceconditions.Differenttypesofvariableclassificationscanbedeterminedbytheirusage,andtheseclassificationsdeterminehowtheyaretobe
implementedortreatedinthemultithreadedloophandler.
LOOP-PRIVATE VARIABLES
Aloopprivatevariableisavariablethatdoesnotpassitsvaluefromoneiterationofthelooptoanother.Itcanactuallybeavariablethatisdeclaredintheloop
itself,anditcanalsobeaninstanceorpubliclyaccessedvariablethatisaccessedbyonlyoneiterationoftheloop.ThisisthecasewiththelookupValues
arrayvariable,whereeachmemberofthearrayisaccessedonlybyoneiterationoftheloop.Althoughthewholearrayisnotloopprivatetoanyiteration,
specificmembersareloopprivatetospecificiterations.
AsshownwiththeSinTableclass,loopprivatevariablesareoftenhandledwithalocalcopyofthevariableineachthread.Sinceeachthreadhasacopy,no
interferencebetweenthethreadsispossible.InthecaseofthelookupValuesarray,thethreadswillrespecttheprivacyoftheotherthreadsbyaccessing
onlytheloopprivateportionsofthearray.
READ-ONLY VARIABLES
Readonlyvariablesarevariableswherevaluesdonotchangeduringtheexecutionoftheloop.Theycanbetrueconstantsorsimplyvariablesthatare
initializedanddonotchangeuntilaftertheloopisprocessed.
Readonlyvariablesrequirenospecialtreatment.Theworkerthreadsdonotneedtohavetheirowncopiesofthevariables,andaccesstothemdoesnot
requiresynchronizationofanytype.
STOREBACK VARIABLES
Storebackvariablesarebasicallyloopprivatevariablesthatareneededaftertheloophasbeencompleted.Forexample,supposethatthelookupValues
arrayrequiressomeextraprocessingaftertheloopisfinished:
publicfloat[]getValues(){
if(lookupValues==null){
floatsinValue=0;
lookupValues=newfloat[360*100];
for(inti=0;i<(360*100);i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
}
lookupValues[0]+=sinValue;
}
returnlookupValues;
}
InthisslightlymodifiedversionoftheSinTableloop,boththesinValuevariableandtheindividualmembersofthelookupValuesarrayarestillloop
privatevariables.Thesetwovariableshavenodatadependencyindifferentiterationsoftheloop.However,inthiscasethesinValuevariableisalsoa
storebackvariable.Sincethevariableisimportantaftertheloophascompleted,itmustbesettothevalueitwouldhavehadiftheloophadruninthecorrect
order.ThemembersofthelookupValuesarraywerealwaysconsideredasstorebackvariables,butsincenoindividualcopieswerekept,therewaslittle
needtomakethisextradistinction.
Here'showwecanhandlethestorebackvariable:
packagejavathreads.examples.ch15.example4;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsGuidedLoopHandler{
privatefloatlookupValues[];
privatefloatsinValue;
publicSinTable(){
super(0,360*100,100,12);
lookupValues=newfloat[360*100];
}
publicvoidloopDoRange(intstart,intend){
floatsinValue=0;
for(inti=start;i<end;i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
}
if(end==endLoop)
this.sinValue=sinValue;
}
publicfloat[]getValues(){
loopProcess();
lookupValues[0]+=sinValue;
returnlookupValues;
}
}
ThesinValuevariableisstilltreatedasaloopprivatevariable.However,sincethisvariableisreallyastorebackvariable,weneedtostorethe"last"value
ofthisvariable.Sincethealgorithmisnowexecutedinamultithreadedmanner,thelastiterationisnotnecessarilythelastvalueassignedtothevariablebya
thread.
Athreadmustcheckthatithasexecutedthelastchunkoftheloopbeforecopyingthevalueofitsloopprivatecopytotheglobalcopy.Alsonotethatno
synchronizationisnecessary.Sinceonlythelastiterationiscopied,onlyonethreadisexecutingthecode,andnoraceconditionispossible.
REDUCTION VARIABLES
Obviously,itisnotpossibletomakeeveryvariablealoopprivatevariablesincetherearecaseswhererealdatadependenciesexistbetweendifferentiterations
oftheloop.Becauseofthesedatadependencies,differentthreadsexecutingdifferentiterationsmightinterferewitheachotherduringexecution.Wecallthese
typesofvariablessharedvariablessincetheyaresharedbetweeniterationsoftheloop.
Sharedvariableshavemanyproblems.Thefirstistheraceconditionsthatexistwhendifferentthreadsaccessthevariablesimultaneously.Thesecondisthat
thevalueofavariablemaydependontheorderinwhichitisprocessed.Inthefirstcase,wecansimplyusesynchronizationtechniquestopreventtherace
conditionsfromexisting.Thesecondcaseposesamuchgreaterproblem.
However,whatiftheorderdoesnotmatter?Wewillbeabletoprocesstheloopinanyorderandwillsimplyhavetosynchronizeaccesstotheshared
variable.Forexample,assumethatwealsoneedtocalculatethesumofourSinTable:
publicfloat[]getValues(){
for(inti=0;i<(360*100);i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
sumValue+=lookupValues[i];
}
returnlookupValues;
}
Inthiscase,thesumValuevariableisclearlynotaloopprivatevariable.ThevalueofsumValueispassedfromoneiterationtoanother,andthecorrect
resultrequiresthisdependencytoexist.However,thesumValuevariableisusefulonlyaftertheloopcompletes.Theiterationssimplyaddtotherunning
totalsubtotalsorotherorderbasedrequirementsarenotnecessary.Furthermore,additionitselfisorderindependent:itispossibletoaddabunchof
numbersinanyorder,andthefinalresultisthesame.
ThesumValuevariableisareductionvariable.Itmuststillbesharedamongthethreads,butsinceorderdoesnotmatter,thissharingonlyrequires
synchronizationtopreventraceconditions:
packagejavathreads.examples.ch15.example5;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsGuidedLoopHandler{
privatefloatlookupValues[];
publicfloatsumValue;
publicSinTable(){
super(0,360*100,100,12);
lookupValues=newfloat[360*100];
}
publicvoidloopDoRange(intstart,intend){
floatsinValue=0;
for(inti=start;i<end;i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
synchronized(this){
sumValue+=lookupValues[i];
}
}
}
publicfloat[]getValues(){
loopProcess();
returnlookupValues;
}
}
RaceconditionsinthisexamplearepreventedbyusingthesynchronizationlockoftheSinTableinstance.Ifwehavemanyreductionvariablesthatarenot
dependentoneachotherandwecannotstorethemallatthesametime,itmightbeabetterideatohaveseparatesynchronizationlocksorexplicitinstances
ofLockinterfacesforeachreductionvariable.
Furthermore,wearesynchronizingwitheachiterationoftheloop.Thisisnotveryefficient.Itisbettertoassignthevaluetoloopprivatevariablesandonly
synchronizethefinalsummedvalueoftherangetothereductionvariable.Bydoingthis,weareremovingmostoftheneedforsynchronization,whichcan
drasticallyaddtotheparallelizationofthethreads:
packagejavathreads.examples.ch15.example6;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsGuidedLoopHandler{
privatefloatlookupValues[];
publicfloatsumValue;
publicSinTable(){
super(0,360*100,100,12);
lookupValues=newfloat[360*100];
}
publicvoidloopDoRange(intstart,intend){
floatsinValue=0.0f;
floatsumValue=0.0f;
for(inti=start;i<end;i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
sumValue+=lookupValues[i];
}
synchronized(this){
this.sumValue+=sumValue;
}
}
publicfloat[]getValues(){
loopProcess();
System.out.println(sumValue);
returnlookupValues;
}
}
Inthisnewexample,wearedoingatwostagereductionofthevalues.WearereducingthevalueofeachiterationtothelocalcopyofthesumValuevariable,
andthenwearereducingthislocalcopytotheactualreductionvariable.SincethelocalcopyofthesumValuevariableisloopprivate,synchronizationisnot
necessary.Synchronizationisstillnecessarywhenaddingtothereductionvariable.However,thisisnowdoneonceperrangeinsteadofonceperiteration.
Areductionvariableisagoodcandidateforanatomicvariable.YoucouldusetheAtomicDoubleclassfromChapter5tostorethesumValuevariablein
thisexample.We'lltestthislaterinthechapter,andyoucanconsulttheonlinesourcecodetoseeexactlyhowthatworks.
Allreductionvariablesarestorebackvariables.Thereisnoneedtohavespecialstorebackhandlinglogicforreductionvariables.
SHARED VARIABLES
Originally,allvariablesinthelooparesharedvariablessinceallvariablescanbeaccessedbyallthethreadsthatareexecutingtheloop.Asweparallelizethe
loop,wecanquicklyclassifythesharedvariablesthatarealsoreadonlyvariables.Wecanalsoreclassifythosevariablesthatareloopprivatevariables.Ofthe
remainingsharedvariables,itmaybepossibleeithertoconvertthemtoloopprivatevariablesortoclassifythemasreductionvariables.
Unfortunately,insomecasesasharedvariablecannotbeclassifiedasanythingbutasharedvariable,andthisiswhereourtechniquefailstowork.Asmuch
aswewouldliketoconvertanylooptoruninamultithreadedenvironment,notallalgorithmscanberedesignedtoruninaparallelenvironment.
Theotherproblemwithsharedvariablesisthesideeffect.Forexample,ifweneedtosaveeachofthesubtotalsofthesumValuevariable,itcannotbe
treatedasareductionvariablesincethechangesinthevariablearealsoimportant.Ifwehavetoprintthesubtotalsduringtheloop,notonlywillthe
intermediateresultsbeoutoforder,buttheintermediateresultswillbedifferent.
Whenvariableclassificationisnotenoughforparallelization,wehaveothertechniquesthatcanhelp.Theymaynotsolveeverycase,butwithexperience,
moreandmoreloopscanbeconvertedtoruninamultithreadedenvironment.
LOOP DISTRIBUTION
Inmanycases,onlyasmallportionofalargecomplexloopcontainscodethatmustbeexecutedsequentially.Itmaybepossibletoseparatethelargecomplex
loopintotwoseparateloops.Oncethecomplexloopisseparatedintotwoloopsoneloopcontainingthecodethatcanbeparallelized,theothercontaining
thesequentialcodewecanthenparallelizeaportionoftheoriginalloop.Wemayevenbeabletorunthesequentialloopinparallelwiththeloopthatcanbe
threaded.
ReturningtoourSinTableexample,let'sassumethatweneedtogeneratearunningsubtotalinadditiontoatotal:
publicfloat[]getValues(){
for(inti=0;i<(360*100);i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
if(i==0){
sumValues[0]=lookupValues[0];
}else{
sumValues[i]=lookupValues[i]+lookupValues[i1];
}
}
returnlookupValues;
}
ThesumValuesarrayvariableisdefinitelyasharedvariable.ThemembersofthesumValuesvariablearealsosharedinthatsomeofthemareaccessedby
twodifferentthreads.Furthermore,theordermatters.Itisnotpossibleforonethreadtostartachunkbeforethethreadthatisworkingonthepreviouschunk
isfinished.
Wecansolvethatproblemlikethis:
packagejavathreads.examples.ch15.example7;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsGuidedLoopHandler{
privatefloatlookupValues[];
publicfloatsumValues[];
publicSinTable(){
super(0,360*100,100,12);
lookupValues=newfloat[360*100];
sumValues=newfloat[360*100];
}
publicvoidloopDoRange(intstart,intend){
floatsinValue=0.0f;
for(inti=start;i<end;i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
}
}
publicfloat[]getValues(){
loopProcess();
sumValues[0]=lookupValues[0];
for(inti=1;i<(360*100);i++){
sumValues[i]=lookupValues[i]+lookupValues[i1];
}
returnlookupValues;
}
}
Whileitisnotpossibletoparallelizetherunningsubtotalwithoutdrasticallychangingthealgorithm,wecanquicklyconverttheloopintotwoseparateloops.
Thefirstloopcontainsthethreadablecode,andthesecondprocessesthesubtotal.Oncethisisaccomplished,wecanthenthreadthefirstloopwithout
changingthesecond.InthenewSinTableclass,wehavemovedtherunningsubtotalcodetoaseparateloop.Thisseparatelooprunsonasinglethread,
onlyafterthefirstloopisprocessed.
Considerthepotentialbenefitbeforeapplyingthistechnique.Sincealargeportionoftheloopmayberunninginasinglethread,theperformancegainmay
notjustifytheeffortinvolved.Inmostcases,calculationsofthesubtotalaresmallconsideringtheeffortofthemaincalculation,andtheperformancepenalty
maybesmallincomparison.
LOOP ISOLATION
Manyprogramsdonotcontainasinglelargeloop.Evenifaparticularloopisdeterminedtobeunparallelizable,theremaybeotherloopsintheprogram.
Eveniftheseotherloopscannotbeparallelized,wemaybeabletoruneachseparateloopinadifferentthread.
Althoughthemanyloopsmaybeverycomplex,withlargedatadependenciesbetweeniterations,theremaybefewdatadependenciesbetweenthedifferent
loops.Itmaybepossibletoisolatetheindividualloopsthemselvesandrunthemeachinaseparatethread.Withthistechnique,loadbalancingisnolonger
possible.Afterall,iftheprogramcontainsfourmajorloopsandyouwereabletoisolatethemall,itisstillimpossibletodistributethesefourloopsamong
twelveprocessors.
LOOP INTERCHANGE
MultilayeredloopsareaprimecauseofCPUboundapplicationsthatrunforalongperiodoftime.Thiscouldbeloopsthataredirectlyinsideofotherloops
or,morelikely,loopsthatcallmethodsthatcontainloops.Thisscenarioissocommonthatweexamineinnerloopthreadinglaterinthischapter.Fornow,
hereisasimplecasetolookfor:
publicfloat[][]getValues(){
for(inti=0;i<360;i++){
lookupValues[0][i]=0;
}
for(intj=1;j<1000;j++){
for(inti=0;i<360;i++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=lookupValues[j1][i]*(float)j/180.0f;
}
}
returnlookupValues;
}
Formultilayeredloops,itisgenerallymoreprofitabletothreadtheouterloopinsteadoftheinnerone.Itisnotnecessarytothreadboththeinnerandouter
loopbecausethreadingeitheroneshouldusealltheprocessors.Iftheouterloopisthreaded,threadingtheinnerloopdoesnotprovideanyfurtherspeedup
sincetherearenomoreprocessorstoruntheextrathreads(andviceversa).Thereasonweprefertothreadtheouterloopisthatthereisanoverheadin
creating,destroying,andsynchronizingamongthemanythreads.Bythreadingtheouterloop,wecreateanddestroythethreadsonceandsynchronizeonlyat
acoarselevelconsequently,lesssynchronizationshouldbenecessary.
Inthisnewversionofthetablecalculation,wearenowworkingonatwodimensionaltable.Threeloopsareusedduringthiscalculation.However,thefirst
loopismerelysettingthefirstrowofvaluestozero.Thenexttwoloopsareactuallyapairofmultilayeredloops.Thealgorithmisloopingtheprocessing
fromrowtorow,executingtheinnerloopthatisprocessingthevaluestobestoredinthedifferentcolumns.
Theprobleminthiscaseisadatadependencybetweentherowsthemselves.Becausethecalculationatanyrowisdependentonthecalculationofthe
previousrow,themembersofanycolumninthelookupValuesarraycannotbeconsideredormadeloopprivate.Theinnerloopcanbeparallelized
withnoproblemsincetherearenodatadependenciesbetweentheiterations.Theonlyrequirementisthattheinnerloopmustassumethattheouterloopran
inthecorrectorderthisrequirementisfinesincewearenotthreadingtheouterloop.
However,wecouldalsorewriteouroriginalcodeasfollows:
publicfloat[][]getValues(){
for(inti=0;i<360;i++){
lookupValues[0][i]=0;
}
for(inti=0;i<360;i++){
for(intj=1;j<1000;j++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=lookupValues[j1][i]*(float)j/180.0f;
}
}
returnlookupValues;
}
Inthisexample,theloopsareinterchanged.Insteadofworkingfromrowtorow,wecanworkfromcolumntocolumn.Theinnerloopcanthenprocessthe
datafromrowtorow.Byinterchangingtheloops,theinnerloopisnolongerthreadablebecauseofthedatadependencybetweenthemembersofthecolumns
inthelookupValuesarray.However,theouterloopisnowthreadable.Oncetheouterloophasbeenthreaded,thereisnolongerareasontothreadtheinner
loop.Sinceitismoreprofitabletothreadanouterloopthananinnerloop,thissimplechangepriortomultithreadinggivesusabetterreturnonour
developmenttimeinvestment.
Unfortunately,althoughloopswithinloopsarecommon,thisexamplemaynotbe.Thereisgenerallysetupcodeforaninnerloop,andtheremaybemultiple
loopsthatarerunsequentiallywithintheouterloop,ortheinnerloopmaybeinsideanothermethodthatiscalledfromtheouterloop.Thedatadependencies
maybesuchthataloopinterchangedoesnotsolvetheproblem.
Havinganinnerloopthatisthreadableinanouterloopthatisnotthreadableiscommon.Weexamineinnerloopthreadinginmoredetaillaterinthischapter.
LOOP REIMPLEMENTATION
Asyoumayhavenoticed,theloophandlerthatwehavedevelopedisfairlyrestrictive.Itappliesonlytoforloops,therangeoftheloopmustbeknownprior
toexecution,itworksonlywithintegersasitsindex,andithasanintervalofonlyonebetweeniterations.Whilesomeoftheserestrictionsarebecausewe
havenotimplementedsupportforcertainfeaturesintheloophandler,themaincauseisthatitisdifficult,ifnotimpossible,toimplementanalgorithmthat
canhandleallgenericloops.
Ifallelsefailsduringlooptransformation,programmingexperienceisstillveryuseful.Awhileoradoloopmaybeconvertedtoaforloop.Thestartand
enditerationsmaybecalculatedpriortoloopexecution.Codemaybemovedfrom,into,orbetweenloops,toallowotherlooptransformationstooccur.Code
changescanalsocausevariableclassificationstochange.Asharedvariablemaybereclassifiedasloopprivateorasareductionvariablebecauseofhowitis
usedinaloop.
Unfortunately,successisneverguaranteed.Thegoalistobalancetheeffortofdevelopmentwiththeaccelerationthatmaybegained.Itmaytakedaysto
implementachangethatachievesonlyoneortwopercentacceleration.Afterall,ifunlimitedeffortwereallowed,wewouldredesignthewholeprogram
fromscratch.
Inner-Loop Threading
Theissuesthatwehavediscussedsofardonotchangewhentheloopsarenested:ifyouapplythetechniquesonlytotheinnerloop,theywork.However,
someother,verysubtleissuesmayapplytoinnerloops.Let'sreturntoourtwodimensionalSinTable.Asmentioned,aloopinterchangeshouldallowthe
outerlooptobethreaded.However,insteadofthelooptransformation,let'strytothreadtheinnerloop:
publicfloat[][]getValues(){
for(inti=0;i<360;i++){
lookupValues[0][i]=0;
}
for(intj=1;j<1000;j++){
for(inti=0;i<360;i++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=lookupValues[j1][i]*(float)j/180.0f;
}
}
returnlookupValues;
}
Thefirstvariabletoclassifyistheouterloopindexvariable,j.Wemustclassifythisvariablesinceitisusedinsidetheinnerloop.Inthiscase,jisclassified
asareadonlyvariable.Atfirstglance,thisdoesnotmakesense:howcouldanindexvariablebereadonly?Wemustonlylookatthescopethatweare
attemptingtothread.Duringtheexecutionoftheinnerloop,thevariablehasasinglevaluethatdoesnotchangethroughouttheentireexecutionoftheloop.
WhilethelookupValuesarrayvariableisasharedvariable,theelementscanbeclassifiedasloopprivate.Sinceeachiterationoftheloopaccessesa
differentmemberofthearraybasedontheloopindexandthereadonlyvariablej,itsmembersmaybeconsideredloopprivate.Themembersofthe
lookupValuesarrayarealsoconsideredstorebackvariables.Sincewearenotcreatingalocalcopyofthesevariables,thereisnoneedtostorethevariables
back.
ThelasttwovariablessinValueandiaresimplyclassifiedasloopprivatevariables,andseparatecopiesarecreatedforeachthread.Neitherofthese
variablesisusedaftertheloophascompleted,sostorebackhandlingisnotnecessary.
Theloopschedulerischosenbyexaminingthealgorithminsidetheinnerloopitself.Inthiscase,thereisnothingthatshouldcauseanyiterationtoexecute
longerthananyotheriteration.Choosingthedefaultstaticorchunkschedulerisprobablybest.However,thereshouldbenoharminchoosingeitherthe
selforguidedselfscheduler.
Oncethesetasksarecompleted,theloopisthreadedbyusingtheloophandlerasusual.However,aslightcomplicationarises:comparedwiththeouterloop,
theinnerloopisexecutedmanymoretimes.Thismeansmanymoretimesthethreadcreationanddestructionoverhead.Furthermore,theloophandleris
designedasa"oneuse"object.Anewloophandlermustbecreatedforeachiterationoftheouterloop.Althoughusingtheloophandlerworkswithoutany
problems,theoverheadmaybemoresignificantthanforthreadingahigherlevelloop.
Wecanpartiallyovercomethiscomplicationasfollows:
packagejavathreads.examples.ch15;
importjava.util.concurrent.*;
publicclassPoolLoopHandlerimplementsRunnable{
protectedstaticclassLoopRange{
publicintstart,end;
}
protectedstaticclassPoolHandlerFactoryimplementsThreadFactory{
publicThreadnewThread(Runnabler){
Threadt=newThread(r);
t.setDaemon(true);
returnt;
}
}
staticprotectedThreadPoolExecutorthreadpool;
staticprotectedintmaxThreads=1;
protectedintstartLoop,endLoop,curLoop,numThreads;
synchronizedstaticvoidgetThreadPool(intthreads){
if(threadpool==null)
threadpool=newThreadPoolExecutor(
1,1,
50000L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue<Runnable>(),
newPoolHandlerFactory());
if(threads>maxThreads){
maxThreads=threads;
threadpool.setMaximumPoolSize(maxThreads);
threadpool.setCorePoolSize(maxThreads);
}
}
publicPoolLoopHandler(intstart,intend,intthreads){
numThreads=threads;
getThreadPool(numThreads);
setRange(start,end);
}
publicsynchronizedvoidsetRange(intstart,intend){
startLoop=start;
endLoop=end;
reset();
}
publicsynchronizedvoidreset(){
curLoop=startLoop;
}
protectedsynchronizedLoopRangeloopGetRange(){
if(curLoop>=endLoop)
returnnull;
LoopRangeret=newLoopRange();
ret.start=curLoop;
curLoop+=(endLoopstartLoop)/numThreads+1;
ret.end=(curLoop<endLoop)?curLoop:endLoop;
returnret;
}
publicvoidloopDoRange(intstart,intend){
}
publicvoidloopProcess(){
reset();
FutureTaskt[]=newFutureTask[numThreads];
for(inti=0;i<numThreads;i++){
t[i]=newFutureTask(this,null);
threadpool.execute(t[i]);
}
for(inti=0;i<numThreads;i++){
try{
t[i].get();
}catch(ExecutionExceptionee){
thrownewRuntimeException(ee.toString());
}catch(InterruptedExceptionie){
thrownewInterruptedException(ie.toString());
}
}
}
publicvoidrun(){
LoopRangestr;
while((str=loopGetRange())!=null){
loopDoRange(str.start,str.end);
}
}
}
ThefactthatouroriginalLoopHandlerclasscanbeusedonlyonceismerelyadesignflaw.Theloopindexcanneverbesetbacktothestartoftheloopnor
cantherangeoftheloopbechanged.Tofixthis,wesimplyaddtwonewmethods,reset()andsetRange(),thatresettheindexbacktothestartofthe
loopandspecifynewrangesfortheloop.Toavoidcreatingalotofthreads,weusethethreadpoolexecutorwelookedatinChapter10.Insteadofcreating
threadsintheloopProcess()method,thismethodnowassignsthetaskstothethreadsinathreadpool.Wecanthensimplywaitforallthethreadsinthe
pooltocompletetheirassignedtasks.Thisallhelpssomewhat,butthesynchronizationthatwehaveintroducedintothecalculationwillhaveaneffectonthe
ultimateaccelerationofourprogram.
Wecanimplementotherschedulingmodelsinthepoolhandlerquiteeasily:
packagejavathreads.examples.ch15;
publicclassPoolSelfLoopHandlerextendsPoolLoopHandler{
privateintgroupSize;
publicPoolSelfLoopHandler(intstart,intend,
intsize,intthreads){
super(start,end,threads);
setSize(size);
}
publicsynchronizedvoidsetSize(intsize){
groupSize=size;
reset();
}
protectedsynchronizedLoopRangeloopGetRange(){
if(curLoop>=endLoop)
returnnull;
LoopRangeret=newLoopRange();
ret.start=curLoop;
curLoop+=groupSize;
ret.end=(curLoop<endLoop)?curLoop:endLoop;
returnret;
}
}
What'sinterestinghereisthesimilaritytoouroriginalSelfLoopHandlerclass.However,tobemoreconfigurable,wehavemodifiedthehandlertoallow
theextraparameters,suchasthechunksize,tobechanged.
Here'showweuseournewhandler:
packagejavathreads.examples.ch15.example8;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsPoolLoopHandler{
privatefloatlookupValues[][];
privateintj;
publicSinTable(){
super(0,360,12);
lookupValues=newfloat[1000][];
for(intj=0;j<1000;j++){
lookupValues[j]=newfloat[360];
}
}
publicvoidloopDoRange(intstart,intend){
floatsinValue=0.0f;
for(inti=start;i<end;i++){
sinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=lookupValues[j1][i]*(float)j/180.0f;
}
}
publicfloat[][]getValues(){
for(inti=0;i<360;i++){
lookupValues[0][i]=0;
}
for(j=1;j<1000;j++){
loopProcess();
}
returnlookupValues;
}
}
ToimplementtheSinTableclass,weplacethecodefromtheinnerloopintheloopDoRange()methodandthencalltheloopProcess()methodto
processtheinnerloop.Sincethejindexvariableisareadonlysharedvariable,itisnowaninstancevariableoftheSinTableclass.
Havingaloophandlerthatcanbeusedmorethanonceisalsoveryimportant.Ifweusetheearlierversionoftheloophandler,wewillhavetocreateanew
instanceoftheloophandlerforeachinnerloopthatweexecute.Thismeansthatthecodefortheouterloopandtheinnerloopcannotbeinthesameclass.
Furthermore,wewillneedtopassareferencetothejvariableandlookupValuesarraytoeachinstancesincethesearesharedbetweenthedifferentinner
loophandlers.
Loop Printing
Loop Printing
ThetaskofsendingastringtoafileorthedisplayisanI/Oboundtask.Usingmultithreadedtechniquesonaloopofoutputdoesnotmakesense.Sincethe
operationisI/Obound,thethreadsspendmostoftheirtimewaiting,andthereislittledifferenceinhaving1or12processorsavailabletorunwaitingthreads.
Furthermore,theorderoftheoutputisimportant.Datathatiswrittentoafileorthedisplayiseventuallyreadbyapersonoranotherprogram.Theoutput
mustlookthesamewhetherthecalculationisdoneasasingleormultithreadedprogram.
However,whatiftheprintingportionoftheloopissmallwhencomparedwiththemathematicalcalculation?IfenoughoftheloopisCPUintensive,itmight
besillytoabandonanattemptatparallelizingtheloopjustbecauseitcontainsaprintln()methodcall.Theonlyproblemthatneedstobesolvedisthe
orderingoftheoutput.Thiscanbedonebyatwostepprintingprocess.Insteadofprintingdirectlytothedisplayorfile,theprogramcanprinttoavirtual,
memorybaseddisplayalongwithanindexusedtoordertheoutput.Whentheprocessingoftheloophascompleted,theoutputcanthenbesenttothe
displayorfile,usingtheindexinformationtoensurethatthedataissentinthecorrectorder.
Let'sreexamineourSinTableloop:
publicsynchronizedfloat[]getValues(){
if(lookupValues==null){
for(inti=0;i<(360*100);i++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
System.out.println(""+i+""+lookupValues[i]);
}
}
returnlookupValues;
}
InthisnewversionofthegetValues()method,wearealsoprintingthetabletostandardoutput.Obviously,thissimpleexamplecanbetransformedwith
aloopdistributiontotwoseparateloops,butlet'sassumethattheprintingprocessishighlyintegratedintothealgorithmandthelooptransformationisnot
possible.
Tosolvethisproblem,we'llusethisclass:
packagejavathreads.examples.ch15;
importjava.util.*;
importjava.io.*;
publicclassLoopPrinter{
privateVectorpStorage[];
privateintgrowSize;
publicLoopPrinter(intinitSize,intgrowSize){
pStorage=newVector[initSize];
this.growSize=growSize;
}
publicLoopPrinter(){
this(100,0);
}
privatesynchronizedvoidenlargeStorage(intminSize){
intoldSize=pStorage.length;
if(oldSize<minSize){
intnewSize=(growSize>0)?
oldSize+growSize:2*oldSize;
if(newSize<minSize){
newSize=minSize;
}
VectornewVec[]=newVector[newSize];
System.arraycopy(pStorage,0,newVec,0,oldSize);
pStorage=newVec;
}
}
publicsynchronizedvoidprint(intindex,Objectobj){
if(index>=pStorage.length){
enlargeStorage(index+1);
}
if(pStorage[index]==null){
pStorage[index]=newVector();
}
pStorage[index].addElement(obj.toString());
}
publicsynchronizedvoidprintln(intindex,Objectobj){
print(index,obj);
print(index,"\n");
}
publicsynchronizedvoidsend2stream(PrintStreamps){
for(inti=0;i<pStorage.length;i++){
if(pStorage[i]!=null){
Enumeratione=pStorage[i].elements();
while(e.hasMoreElements()){
ps.print(e.nextElement());
}
}
}
}
}
Theloopprinterisimplementedusingatwodimensionalvector.Thefirstdimensionisusedtoseparatetheoutput.Thisoutputindexcouldberelatedtothe
indexoftheactualloop,ortoachunkoftheloop,oritcouldevenbeacombinationofmultipleloopindices.Inanycase,anoutputindexshouldnotbe
assignedtomorethanonethreadsincetheorderinginsideanindexedvectorisbasedonit.Theseconddimensionholdsthestringsthataresenttotheoutput.
[ 2 ]
Sincetheindiceshavealreadyorderedthestringstobeprinted,thisdimensionisjustusedtostorethemanystringsthataresenttothisindex.
Printinganobjecttothevirtualdisplayisdonewiththeprint()andprintln()methods.Alongwiththeobjecttobeprinted,theprogrammustsupply
anindexasareferenceoftheprintingorder.Thesemethodssimplystoreareferencetothestringssothattheymaybeprintedatalatertime.Thesecond
phaseoftheprintingprocessisdonebythesend2stream()method.Oncetheloophascompleted,acalltothismethodprintstheresulttotheoutput
specified.
Here'showtousetheLoopPrinterclass:
packagejavathreads.examples.ch15.example9;
importjavathreads.examples.ch15.*;
publicclassSinTableextendsGuidedLoopHandler{
privatefloatlookupValues[];
privateLoopPrinterlp;
publicSinTable(){
super(0,360*100,100,12);
lookupValues=newfloat[360*100];
lp=newLoopPrinter(360*100,0);
}
publicvoidloopDoRange(intstart,intend){
for(inti=start;i<end;i++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[i]=sinValue*(float)i/180.0f;
lp.println(i,""+i+""+lookupValues[i]);
}
}
publicfloat[]getValues(){
loopProcess();
lp.send2stream(System.out);
returnlookupValues;
}
}
Theloopprinteriscreatedpriortotheloop,allprintingthatwaspreviouslysenttoafileorthedisplayissenttotheloopprinter,andthesend2stream()
methodiscalleduponcompletionoftheloop.Sincetheloopprintersendsalltheinformationtoonetarget,multipleloopprintersmustbecreatediftheloop
printstodifferentstreams.
Alsonotethatweconstructedtheloopprinterwiththeindexsizeasitsinitialsize.Theloopprinteriswrittentoexpandtoanysize,sothisextradefinitionis
notnecessary.Wewanttoavoidexpandingthesizebecausethisoperationnotonlyrequiresthemethodtobesynchronized,butalso,dependingonthesize,
takessometimetoexecute.Theprint()andprintln()methodsmustalsobesynchronized.Thisservestwopurposes:First,itallowsthearraysizeto
beincreasedwithoutaracecondition.Second,itallowsthemethodstoworkalthoughtheprintorderisnolongerguaranteedifanindexisassignedto
twothreads.Iftheloopprinterismodifiedsoasnottoallowthearraytobeenlarged,andifitisassumedthatdeveloperswillnotassigntwothreadstothe
sameindex,synchronizationatthislevelwillnolongerbenecessary.
Multiprocessor Scaling
Scalingisatermthatissometimesoverused.Itcanapplytohowmanyprogramsacomputercanexecutesimultaneously,howmanydiskscanbewrittento
simultaneously,orhowmanycreamcheesebagelorderscanbeprocessedbythelocalbagelshop'screw.Whentheoutputcannotbeincreasednomatterhow
manyresourcesareadded,thislimitisgenerallythevalueusedtospecifywhatsomethingscalesto.Iftheovencannotproducemorebagelsperhour,itdoes
notmatterhowmanypeopleareaddedtotheassemblyline:therateofbagelscannotexceedtherateproducedbytheoven.Thescalinglimitcanalsobe
controlledbymanyotherfactors,suchastheratethatthecreamcheesecanbeproduced,thesizeoftherefrigerators,orevenbythesuppliersforthebagel
shop.
Inthischapter,whenwerefertothescalabilityofamultithreadedprogram,wearereferringtothelimitonthenumberofprocessorswecanaddandstill
obtainanacceleration.Addingmorethanthislimitdoesnotmaketheprogramrunfaster.Obviously,howaprogramscalesdependsonmanyfactors:the
operatingsystem,theJavavirtualmachineimplementation,thebrowserorapplicationserver,andtheJavaprogramitself.Thebestaprogramcanscaleis
basedonthescalabilitylimitsofallofthesefactors.
ForperfectCPUboundprogramsinaperfectworld,wecouldexpectperfectscaling:addingasecondCPUwouldhalvetheamountoftimethatittakesthe
programtorun,addinganotherCPUwouldreducethetimebyanotherthird,andsoon.Evenfortheloopbasedprogramswe'veexaminedinthischapter,
however,theamountofscalingisalsolimitedbytheseimportantconstraints:
Setuptime
Acertainamountoftimeisrequiredtoexecutethecodeoutsideoftheloopthatisbeingparallelized.Thisamountoftimeisindependentofthenumberof
threadsandprocessorsthatareavailablebecauseonlyasinglethreadexecutesthatcode.
Newsynchronizationrequirements
Inparallelizingtheloopsofthischapter,we'veintroducedsomeadditionalbookkeepingcode,someofwhichissynchronized.Becausesomeoftheseare
contendedlocks,thisincreasesthetimerequiredtoexecutethecode.
Serializationofmethods
Somemethodsinourparallelizedcodemustrunsequentiallybecausetheyaresynchronized.Contentionforthelockassociatedwiththesemethodsalso
affectsthescalabilityofourparallelizedprograms.
Ifweviewthesetuptime,synchronizationtime,andtimerequiredtoexecutetheserializedmethodsasapercentageofthetotalrunningtime,theremaining
timeistheamountofcodethatisparallelized.Themaximumamountofscalingthatwe'llseeisgivenbyAmdahl'sLaw:
Here,Sisthescalingwe'llsee,assumingthatF%ofcodeisparallelizedoverNprocessors.If95%ofthecodeisparallelizedandwehaveeightprocessors
available,thecoderunsin16.8%oftheoriginaltimerequired(.05+.95/8).However,whenweintroducecodetocalculateloopranges(oranyothercode),
we'veactuallyincreasedtheamountofserializedcode,soFcouldpotentiallybeanegativenumber.Inthatcase,ourparallelizedcodetakeslongertorunthan
ouroriginalcode.
Whatsortofscalingcanweexpectfromthetechniquesofthischapter?Toanswerthisquestion,wetestseveralimplementationsofoursampledoubleloop:
publicfloat[][]getValues(){
for(inti=0;i<360;i++){
lookupValues[0][i]=0;
}
for(intj=1;j<1000;j++){
for(inti=0;i<360;i++){
floatsinValue=(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=lookupValues[j1][i]*(float)j/180.0f;
}
}
returnlookupValues;
}
Tomaketestingeasier,weusethefollowingclassandinterfacetobuildasystembywhichwemaytestvariousloophandlers.
packagejavathreads.examples.ch15;
importjava.util.*;
importjava.text.*;
importjava.io.*;
publicclassScaleTest{
privateintnIter=200;
privateintnRows=2000;
privateintnCols=200;
privateintnThreads=8;
Classtarget;
ScaleTest(intnIter,intnRows,intnCols,intnThreads,
StringclassName){
this.nIter=nIter;
this.nRows=nRows;
this.nCols=nCols;
this.nThreads=nThreads;
try{
target=Class.forName(className);
}catch(ClassNotFoundExceptioncnfe){
System.out.println(cnfe);
System.exit(1);
}
}
voidchart(){
longsumTime=0;
longstartLoop=System.currentTimeMillis();
try{
ScaleTesterst=(ScaleTester)target.newInstance();
for(inti=0;i<nIter;i++){
st.init(nRows,nCols,nThreads);
System.gc();
longthen=System.currentTimeMillis();
floatans[][]=st.doCalc();
longnow=System.currentTimeMillis();
sumTime+=(nowthen);
}
}catch(Exceptione){
e.printStackTrace();
System.exit(1);
}
longendLoop=System.currentTimeMillis();
longcalcTime=endLoopstartLoop;
System.err.println("Looptime"+sumTime+
"("+((sumTime*100)/calcTime)+"%)");
System.err.println("Calculationtime"+calcTime);
}
publicstaticvoidmain(Stringargs[]){
if(args.length!=5){
System.out.println(
"Usage:javaScaleTesternIternRowsnColsnThreadsclassName");
System.exit(1);
}
ScaleTestsc=newScaleTest(Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2]),
Integer.parseInt(args[3]),
args[4]);
sc.chart();
}
}
WhenweusetheScaleTestclass,wegettwonumbers:thenumberofmillisecondsrequiredtoruntheentireprogram(includinginitialization,whichis
singlethreaded)andthenumberofmillisecondsrequiredtorunjusttheloopcalculation.Wethencomparethesenumberstodeterminethescalabilityof
variousimplementationsofourloophandlingclasses.
Intheremainderofthissection,we'lldevelopexamplesthatusethisclasstoseetheeffectofparallelizationofourloopgiventheconstraintswe'vediscussed
inthischapter.
importjavathreads.examples.ch15.*;
publicclassBasicimplementsScaleTester{
privatefloatlookupValues[][];
intnCols,nRows;
publicvoidinit(intnRows,intnCols,intnThreads){
this.nCols=nCols;
this.nRows=nRows;
lookupValues=newfloat[nRows][];
for(intj=0;j<nRows;j++){
lookupValues[j]=newfloat[nCols];
}
}
publicfloat[][]doCalc(){
for(inti=0;i<nCols;i++){
lookupValues[0][i]=0;
}
for(intj=1;j<nRows;j++){
for(inti=0;i<nCols;i++){
floatsinValue=
(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=
lookupValues[j1][i]*(float)j/180.0f;
}
}
returnlookupValues;
}
}
Thisclasscontainsnothreadingitisthewaythatwewouldnormallyimplementthebasiccalculationwe'reinterestedintesting.Wecomparethisclasswith
thefollowingloophandlerclass:
packagejavathreads.examples.ch15.example10;
importjavathreads.examples.ch15.*;
publicclassGuidedLoopInterchangedimplementsScaleTester{
privatefloatlookupValues[][];
privateintnRows,nCols,nThreads;
privateclassGuidedLoopInterchangedHandler
extendsGuidedLoopHandler{
GuidedLoopInterchangedHandler(intnc,intnt){
super(0,nc,10,nt);
}
publicvoidloopDoRange(intstart,intend){
for(inti=start;i<end;i++){
lookupValues[0][i]=0;
}
for(inti=start;i<end;i++){
for(intj=1;j<nRows;j++){
floatsinValue=
(float)Math.sin((i%360)*Math.PI/180.0);
lookupValues[j][i]=sinValue*(float)i/180.0f;
lookupValues[j][i]+=
lookupValues[j1][i]*(float)j/180.0f;
}
}
}
}
publicvoidinit(intnRows,intnCols,intnThreads){
this.nRows=nRows;
this.nCols=nCols;
this.nThreads=nThreads;
lookupValues=newfloat[nRows][];
for(intj=0;j<nRows;j++){
lookupValues[j]=newfloat[nCols];
}
}
publicfloat[][]doCalc(){
GuidedLoopInterchangedHandlerloop=
newGuidedLoopInterchangedHandler(nCols,nThreads);
loop.loopProcess();
returnlookupValues;
}
}
Thisclassusesoursimpleloophandlertoprocesstheloopnotice,however,thatwe'veinterchangedtheloopsinordertomaketheouterloopthreadable.The
onlineexampleshavesimilarhandlersthatperformasimpleloopinterchangeandaselfguidedloopinterchange.
Table151liststheresultsoftheScaleTestprogramwhenrunwithdifferentimplementationsoftheinterchangedloop:we'veusedchunk,selfscheduled,
andguidedselfschedulingloophandlersinconjunctionwiththecodeweshowedearlier.ThesetestswererunonamachinewitheightCPUs,usingan
iterationcountof200,arowcountof1500,andacolumncountof3000.We'venormalizedtherunningtimeforthebaselineruntobe100sothatother
numberscanbeviewedasapercentage:thebestthatwedoisrunin20%ofthetimerequiredfortheoriginalrun.
Table151.Scalabilityofsimpleloophandlers
Numberofthreads
Totaltime
Looptime
Basic
100%(baseline)
94.0%
108.0%
101.8%
57.5%
51.4%
32.7%
26.7%
20.7%
14.6%
12
23.3%
17.0%
16
21.2%
14.9%
Selfscheduling
111.2%
105.0%
74.3%
68.2%
42.1%
35.9%
25.3%
19.1%
12
25.2%
19.0%
16
25.1%
18.9%
Guidedselfscheduling
108.0%
101.9%
58.7%
52.6%
32.7%
26.6%
20.0%
13.8%
12
21.9%
15.8%
javathreads.examples.ch15.example10.Basic
Chunkscheduling
javathreads.examples.ch15.example10.LoopInterchanged
javathreads.examples.ch15.example10.SelfLoopInterchanged
javathreads.examples.ch15.example10.GuidedLoopInterchanged
12
21.9%
15.8%
16
21.3%
15.0%
Wecandrawafewconclusionsfromthistable:
Theoverheadofsettingupthethreadandloophandlingclassitselfissignificant:itrequires8%to11%moretimetoexecutethatcodewhenonlyasingle
threadisavailable.WewouldnotwanttousethistechniqueonamachinewithonlyoneCPU.
Thescalingoftheloopcalculationitselfisgood.Sincetheoriginalloopaccountedfor94%ofthecode,witheightCPUsthebestthatwecanhopefor
(usingAmdahl'slaw)is17.8%.We'veachieved20%,whichimpliesthat88.5%ofthecodeisnowparallelized:the5%differenceisaccountedforbythe
serializedcallstotheloopGetRange()methodandthefactthateachthreadisprobablynotdoingthesameamountofwork.
Goingpasteightthreadsthatis,thenumberofCPUsavailableyieldsapenalty.ThisispartiallybecausewenowhavethreadscompetingforaCPU,
butitisalsobecauseofthesynchronizationaroundtheadditionalcallstotheloopGetRange()method:there'snowagreaterchancethatthe
synchronizationiscontended.However,notethatwhilethereisapenaltyfor12threads,thepenaltyfor16threadsisless.With12threads,atsomepoints
intimeonly4threadshaveworklefttodo,whichleaves4CPUsidle.
Theguidedselfscheduleristhebestchoiceinthisexample.Thisisnotsurprising:calculationsbasedonsinvaluesdonotalwaysrequirethesame
amountoftime,sothechunkschedulercanbepenalizedbyhavingoneparticularthreadthatrequirestoomuchtime.Thatcontributestoalossofscaling
sincethethreadsdonotendupperformingequalamountsofwork.
Allinall,though,we'veachievedverygoodscalability.
Table152.Scalabilityofloophandlerswithreductionvariables
Numberofthreads
Totaltime
Looptime
Basic
100%(baseline)
93.8%
111.8%
105.5%
59.2%
52.9%
33.6%
27.3%
20.9%
14.6%
12
23.7%
17.3%
16
21.5%
15.0%
Guidedselfscheduling
110.0%
103.6%
58.0%
51.7%
32.7%
26.4%
20.1%
13.8%
12
22.1%
15.8%
16
21.5%
15.1%
Guidedatomicselfscheduling
114.2%
107.8%
60.4%
54.0%
33.8%
27.4%
21.2%
14.9%
12
24.0%
17.5%
javathreads.examples.ch15.example11.Basic
Chunkscheduling
javathreads.examples.ch15.example11.LoopInterchanged
javathreads.examples.ch15.example11.GuidedLoopInterchanged
javathreads.examples.ch15.example11.GuidedAtomicLoopnterchanged
12
24.0%
17.5%
16
21.8%
15.3%
Becausethere'sonlyonereductionvariable,theeffectonscalingisminor.Infact,insomecaseswedidslightlybetterbecausethebaselinenowtakeslonger
toexecute.However,theeffectofmanyreductionvariablescouldpotentiallyaggregateintosomethingmorenoticeable.
Wedidnobetterinfact,slightlyworsebyreplacingthesynchronizedcalltothesumValuewithacalltoourAtomicDoubleclassfromChapter5.In
thistest,theoverheadcomesalmostentirelyfromtheloophandlingratherthanthesynchronizationaftereveryloopcompletion.
Numberofthreads
Totaltime
Looptime
Basic
100%(baseline)
94.7%
100%
94.6%
57.7%
52.0%
38.4%
32.4%
41.5%
35.5%
12
53.2%
47.1%
16
58.2%
52.0%
javathreads.examples.ch15.example12.Basic
Guidedselfscheduling
javathreads.examples.ch15.example12.GuidedLoopInterchanged
Inthistest,westartoutwithsomescaling,throughaboutfourCPUs.EvenatfourCPUs,however,we'renotseeingthesamescalingasinourprevioustests.
BythetimewegettoeightCPUs,theinnerloophasonly375calculations,andtheadditionaloverheadofrepeatedlycallingtheloopGetRange()method
hasovercomeanyadvantagewereceivedbyrunningthesmallloopsinparallel.Thingsgetworseasweaddmorethreads.
Thiseffectbecomesevenmorepronouncedifwerunwithasmallerinnerloopsize.Withonly1000columns,runningwith4threadsrequires72.3%ofthe
originaltime,andrunningwith16threadsnowrequires123.8%oftheoriginaltime.TheloopitselfrunssofastthatthecallstoloopGetRange()(andthe
contentionforitslock)makeourprogramactuallyrunslower.
Aswementioned,threadingofsmallloopsandparticularlyofsmallinnerloopsisnotnecessarilyworthwhile.
A Printing Test
Whatifweaddcodetotheloopthatprintsouttheresultofsomecalculations?WecanstillthreadsuchacaseusingtheLoopPrinterclassthatwe
developedearlier.However,rememberthatweendedoursectionontheLoopPrinterclasswithadiscussionthatwouldenableustoremoveits
synchronization.Becauseinthisparticulartestwealwaysknowthesizeoftheoutputarrayandwecanensurethatthesameindexisnotusedbytwodifferent
threads,wecanrewritetheLoopPrinterclasslikethis:
packagejavathreads.examples.ch15;
importjava.util.*;
importjava.io.*;
//Nonthreadsafeversionofaloopprinter
publicclassLoopPrinterUnsafe{
privateVectorpStorage[];
publicLoopPrinterUnsafe(intsize){
pStorage=newVector[size];
}
publicvoidprint(intindex,Objectobj){
if(pStorage[index]==null){
pStorage[index]=newVector();
}
pStorage[index].addElement(obj.toString());
}
publicvoidprintln(intindex,Objectobj){
print(index,obj);
print(index,"\n");
}
publicvoidsend2stream(PrintStreamps){
for(inti=0;i<pStorage.length;i++){
if(pStorage[i]!=null){
Enumeratione=pStorage[i].elements();
while(e.hasMoreElements()){
ps.print(e.nextElement());
}
}
}
}
}
Thisversionoftheloopprintereliminatesthesynchronizationofourfirstimplementation.Thereisstillsomesynchronizationwhenaddingthestringtothe
vector,butifwesetupthethreadindicescorrectly,thisisalluncontendedsynchronizationandhaslittleeffectonourtime.Itstilltakeslongertoaddstringsto
thesevectorsandthendumpthemoutthantosimplycalltheps.println()method.However,thedifferencebetweenourthreadsafeandthreadunsafe
versionsofthisclassisimportant.Table154liststheresultsthatweobtainedforbothcasescases(usingtheclassesfromexample13intheonlinearchive).
Table154.Scalabilityofloopprinterhandlers
Numberofthreads
Totaltime
Looptime
Basic
100%(baseline)
96.3%
106.7%
99.2%
90.2%
82.7%
83.9%
76.4%
86.0%
78.5%
12
89.3%
81.8%
16
86.5%
78%
Threadunsafeloopprinter
109.2%
101.7%
85.1%
77.6%
75.4%
67.9%
65.2%
57.7%
12
67.7%
60.2%
16
66.4%
58.9%
javathreads.examples.ch15.example13.Basic
Threadsafeloopprinter
javathreads.examples.ch15.example13.GuidedLoopInterchanged
javathreads.examples.ch15.example13.UnsafePrinterInterchanged
Thenumbersinthistableareobtainedfromprintingouttheresultofevery20thcalculation.Evenwhentheloopprinterclassisnotsynchronized,theextra
overheadofalltheobjectmanipulationwithintheprinterclassaddsalotoftimetotheoverallexecutionprintingthestringsinthestoredvectors(whichis
stillasinglethreadedoperation)takesover40%oftheexecutiontime.Inthesynchronizedcase,contentionforthelockspreventsusfromgettingmuch
scalingbenefitatall.Thisisonecasewhereacarefuldesignthatallowsyoutoavoidsynchronizationcanhaveabenefit.
It'sinterestingtocomparetheseresultstoacaseinwhichweprintoutonlyevery1000thcalculation.Nowtheprintingtimenolongerdominatesthe
calculation(seeTable155).
Table155.Scalabilityofloopprinterhandlers
Numberofthreads
Totaltime
Looptime
Basic
100%(baseline)
96.3%
131.5%
112.8%
54.7%
35.9%
42.3%
23.5%
Threadunsafeloopprinter
134.4%
115.7%
54.2%
35.4%
41.8%
23.0%
javathreads.examples.ch15.example13.Basic1000
Threadsafeloopprinter
javathreads.examples.ch15.example13.GuidedLoopInterchanged1000
javathreads.examples.ch15.example13.UnsafePrinterInterchanged1000
Wegetbetterscalabilityhere,thoughstillclearlyworsethanwhenwehadnoprintingatall.Thelessonhereisclear:whenyouwanttogetthemostbenefit
outofrunningcodeinparallel,reducingtheamountofserialcodemakesabigdifferenceinthebenefitsyou'llsee.
Summary
Inthischapter,weexaminedtechniquesthatallowustoutilizemultiprocessormachinessothatourJavaprogramsrunfasteronthosemachines.We
examinedloopsthemostcommonsourceofCPUintensivecodeanddevelopedclassesthatallowtheseloopstoruninamultithreadedfashion.Along
theway,wehaveclassifiedvariables,usedvariousschedulingalgorithms,andappliedsimplelooptransformationstoachievethisparallelization.
Thegoalsherearetowritefastprogramsfromthestart,toincreasetheperformanceofoldalgorithmswithoutredesigningthemfromscratch,andtoprovide
arichsetofoptionsthatcanbeusedforcaseswherehighperformanceisrequired.
Example Classes
ThefirstnineSinTableclassesweshowedshouldmainlybeusedasareference.Theycontaintestingcode,buttheprintedoutputisn'tasinterestingasthe
codeitself.
Examples1013aresomewhatdifferentfromtheexamplesfromearlierchapters.Theseexamplesareusedfortheteststhatproducedthetablesinthis
chapter.Thesetestsareallrunviathesameclass:theScaleTestclass.Oneoftheargumentsrequiredtorunthescaletestisthenameofthetargetclassto
test.Theclassesthatareexecutedinthosetestsarelistedinthetablesshownearlierinthischapter.
Description
MainJavaclass
Anttarget
TableGenerator(Singlethreaded)
javathreads.examples.ch15.example1.SinTable
ch15ex1
TableGenerator(Multithreaded)
javathreads.examples.ch15.example2.SinTable
ch15ex2
TableGenerator(Usingloophandler)
javathreads.examples.ch15.example3.SinTable
ch15ex3
TableGenerator(Handlingreductionvariables)
javathreads.examples.ch15.example4.SinTable
ch15ex4
TableGenerator(Handlingreductionvariables)
javathreads.examples.ch15.example5.SinTable
ch15ex5
TableGenerator(Twostagereduction)
javathreads.examples.ch15.example6.SinTable
ch15ex6
TableGenerator(Handlingsharedvariables)
javathreads.examples.ch15.example7.SinTable
ch15ex7
TableGenerator(Threadinginnerloops)
javathreads.examples.ch15.example8.SinTable
ch15ex8
TableGenerator(Printing)
javathreads.examples.ch15.example9.SinTable
ch15ex9
ScaleTester
javathreads.examples.ch15.ScaleTest
scaleLoopsnRowsnColumnsnThreadsclassname
ch15scale
FortheScaleTestclass,theclassnameargumentappearsintablelistingsearlierinthischapter(e.g.,thefirsttestinTable151istheclass
javathreads.examples.ch15.example10.Basic).Intheanttarget,thepropertiestouseforthedifferentparametersareasfollows:
<propertyname="nThreads"value="10"/>
<propertyname="scaleLoops"value="200"/>
<propertyname="nRows"value="1500"/>
<propertyname="nCols"value="2000"/>
<propertyname="classname"value="javathreads.examples.ch15.example10.Basic"/>
[ 1 ]
Notethatwe'vestartedextendingthePoolLoopHandlerclass,whichisfunctionallyequivalenttotheLoopHandlerclass.We'll
discussthischangelaterinthechapter.
[ 2 ]
Technically,wecouldhavedonethesamethingwithasingledimensionalarrayofstringbuffers.
publicclassBusyFlag{
protectedThreadbusyflag=null;
protectedintbusycount=0;
publicsynchronizedvoidgetBusyFlag(){
while(tryGetBusyFlag()==false){
try{
wait();
}catch(Exceptione){}
}
}
publicsynchronizedbooleantryGetBusyFlag(){
if(busyflag==null){
busyflag=Thread.currentThread();
busycount=1;
returntrue;
}
if(busyflag==Thread.currentThread()){
busycount++;
returntrue;
}
returnfalse;
}
publicsynchronizedvoidfreeBusyFlag(){
if(getBusyFlagOwner()==Thread.currentThread()){
busycount;
if(busycount==0){
busyflag=null;
notify();
}
}
}
publicsynchronizedThreadgetBusyFlagOwner(){
returnbusyflag;
}
}
TheBusyFlagclassimplementsabasic,nofrills,mutuallyexclusivelock.Italsoallowsthelockstobenestedtheownerthreadcanlockthebusyflag
multipletimes.ItismuchsimplerthantheReentrantLockclass.Thereisnointernalsupportforconditionvariables.Thereisnosupportfortimeouts.
Thereisnoconceptoffairnessingrantingthebusyflag.Andourimplementationdoesnotattempttominimizesynchronization.
Simplistically,thepurposeofthisclassistouseJava'sbasicsynchronizationmechanismtoachieve,well,synchronization.Thisallowstheprogramtolockat
anyscopeorforanypurpose.
TheBusyFlagclasscontainsfourmethods.ThetryGetBusyFlag()classisusedtoobtainalock(a.k.a.thebusyflag).Itgrabsthebusyflagifitis
availablewhilereturningfalseiftheflagisalreadyownedbyanotherthread.Italsoallowsnestedlocksbyincrementingacounterifthecurrentthread
alreadyownstheflag.Thesynchronizedkeywordisusedtoprotectagainstraceconditionswhilegrabbingthisflag.
ThegetBusyFlag()methodusesthetryGetBusyFlag()methodtorepeatedlytrygrabbingtheflaguntilitissuccessful.Iftheflagisnotavailable,it
usesthewaitandnotifymechanismtowaitfortheflagtobereturned.ThefreeBusyFlag()methoddecrementsthecounter.Andifthecounteriszero,
thismethoddeclaresthattheflaghasnoownerandnotifiesanythreadsthatarewaitingtograbtheflag.
ThegetBusyFlagOwner()methodismerelyanadministrationmethodthatallowsathreadtodeterminewhoistheownerofthebusyflag.Alsonotethat
duetoaracecondition,theresultthatisreturnedisonlyguaranteednottochangeifthecurrentthreadisreturnedastheownerofthebusyflag.
publicclassCondVar{
privateBusyFlagSyncVar;
publicCondVar(){
this(newBusyFlag());
}
publicCondVar(BusyFlagsv){
SyncVar=sv;
}
publicvoidcvWait()throwsInterruptedException{
cvTimedWait(SyncVar,0);
}
publicvoidcvWait(BusyFlagsv)throwsInterruptedException{
cvTimedWait(sv,0);
}
publicvoidcvTimedWait(intmillis)throwsInterruptedException{
cvTimedWait(SyncVar,millis);
}
publicvoidcvTimedWait(BusyFlagsv,intmillis)
throwsInterruptedException{
inti=0;
InterruptedExceptionerrex=null;
synchronized(this){
//Youmustownthelockinordertousethismethod
if(sv.getBusyFlagOwner()!=Thread.currentThread()){
thrownewIllegalMonitorStateException(
"currentthreadnotowner");
}
//Releasethelock(Completely)
while(sv.getBusyFlagOwner()==Thread.currentThread()){
i++;
sv.freeBusyFlag();
}
//Usewait()method
try{
if(millis==0){
wait();
}else{
wait(millis);
}
}catch(InterruptedExceptioniex){
errex=iex;
}
}
//Obtainthelock(Returntooriginalstate)
for(;i>0;i){
sv.getBusyFlag();
}
if(errex!=null)throwerrex;
return;
}
publicvoidcvSignal(){
cvSignal(SyncVar);
}
publicsynchronizedvoidcvSignal(BusyFlagsv){
//Youmustownthelockinordertousethismethod
if(sv.getBusyFlagOwner()!=Thread.currentThread()){
thrownewIllegalMonitorStateException(
"currentthreadnotowner");
}
notify();
}
publicvoidcvBroadcast(){
cvBroadcast(SyncVar);
}
publicsynchronizedvoidcvBroadcast(BusyFlagsv){
//Youmustownthelockinordertousethismethod
if(sv.getBusyFlagOwner()!=Thread.currentThread()){
thrownewIllegalMonitorStateException(
"currentthreadnotowner");
}
notifyAll();
}
}
TheCondVarclassimplementsabasicconditionvariableforusewiththeBusyFlagclass.Thereisnoconceptoffairnessinnotification.Itisconstructed
separatelyfromtheBusyFlagclassascomparedtoConditionobjects,whicharegeneratedfromtheLockclassviathenewCondition()method.
AndliketheBusyFlagclass,theimplementationdoesn'tattempttominimizesynchronization.
ThepurposeofthisclassistoallowJava'swaitandnotifymechanismtoworkwithexplicitlocking(locksatanyscope).Thisallowstheprogramtohave
conditionvariablesupportfortheBusyFlagclass.Italsoallowsasinglelocktohavemorethanoneconditionvariable,wherethewaitandnotify
mechanismneedsaseparateobjectforeverytypeofnotification.
TheCondVarclassprovidesfourmethodsforwaitingfornotificationthreeofthesemethodscanbeconsideredconveniencemethods.Theprimarymethod
isthecvTimedWait()method.Thismethodfreestheownershipofthebusyflagcompletelyandthenusesthestandardwait()methodtoperformthe
wait.Ifthetimetowaitiszero,thismethodwaitsindefinitelyforthenotification.Otherwise,itusesthetimeoutspecified.Uponreturning,itgrabsthelock
(notethatitmustdothatasmanytimesasthelockwasreleasedtosupportthenestingsemanticsofourBusyFlagclass).Alsonotethatitmaystillwait
uponreceivingnotificationasitcanstillblockwhilereacquiringtheflag.Infact,that'sthecasewithallnotificationbasedtechniques(theConditionclass,
thewaitandnotifymechanism)it'sjustinthiscodethatyouseetheeffectexplicitly.
Twooftheconveniencemethodsallowtheprogramtospecifyatimeoutorwaitindefinitely.Thelastoneallowsyoutospecifyanalternatebusyflagclass
aflagthatisdifferentfromtheonespecifiedduringconstruction.SpecifyinganalternatebusyflagisnotafeaturesupportedbytheConditionclassa
ConditioninstanceistightlyboundtotheLockinstancefromwhichitwasobtained.Thisfeatureallowsnotificationbetweentwogroupsofthreadsthat
areoperatingondifferentlocks.Intermsoffunctionality,thisisaminorenhancementforaveryrareneed.UsingtheConditionclass,acommonLock
objectcouldbecreatedjustfornotificationbetweenthetwogroupsofthreadstoachievethesamething.
ThecvSignal()methodisusedtosendasinglenotificationusingthenotify()method.Aswiththewaitmethods,itisoverloadedtoallowthe
programtospecifyanalternatebusyflag.ThecvBroadcast()methodisusedtosendnotificationstoallthewaitingthreadsusingthenotifyAll()
method.It,too,isoverloadedtoallowtheprogramtospecifyanalternatebusyflag.
publicclassBarrier{
privateintthreads2Wait4;
privateInterruptedExceptioniex;
publicBarrier(intnThreads){
threads2Wait4=nThreads;
}
publicsynchronizedintwaitForRest()
throwsInterruptedException{
intthreadNum=threads2Wait4;
if(iex!=null)throwiex;
if(threads2Wait4<=0){
notifyAll();
returnthreadNum;
}
while(threads2Wait4>0){
if(iex!=null)throwiex;
try{
wait();
}catch(InterruptedExceptionex){
iex=ex;
notifyAll();
}
}
returnthreadNum;
}
publicsynchronizedvoidfreeAll(){
iex=newInterruptedException("BarrierReleasedbyfreeAll");
notifyAll();
}
}
TheBarrierclassisabasic,nofrillsimplementationofabarrier.ImplementationoftheBarrierclasswiththebasicsynchronizationtechniquesis
straightforward.Wesimplyhaveeachthreadthatarrivesatthebarrier(i.e.,thatcallsthewaitForRest()method)callthewait()methodwhilethelast
threadtoarriveatthebarrierhasthetaskofnotifyingallofthewaitingthreads.Ifanyofthethreadsreceivesaninterruption,allofthethreadsreceivethe
sameinterruption.Anothermethod,freeAll(),isalsoprovidedtogenerateaninterruptonallofthethreads.Asanaddedbenefit,athreadnumberis
assignedtothethreadstohelpdistinguishthewaitingthreads.Thelastthreadtoreachthebarrierisassignedthevalueofzero,andanythreadthatreachesthe
barrierafterthebarrierhasbeenreleasedisassignedanegativevalue.Thisindicatesanerrorconditionforthethread.
Thisimplementationofthebarrierisasingleuseimplementation.Oncethebarrierreachesthethreadlimitasspecifiedbytheconstructor,oranerroris
generated,thebarriernolongerblocksanythreads.
importjava.util.*;
classRWNode{
staticfinalintREADER=0;
staticfinalintWRITER=1;
Threadt;
intstate;
intnAcquires;
RWNode(Threadt,intstate){
this.t=t;
this.state=state;
nAcquires=0;
}
}
publicclassRWLock{
privateVectorwaiters;
privateintfirstWriter(){
Enumeratione;
intindex;
for(index=0,e=waiters.elements();
e.hasMoreElements();index++){
RWNodenode=(RWNode)e.nextElement();
if(node.state==RWNode.WRITER)
returnindex;
}
returnInteger.MAX_VALUE;
}
privateintgetIndex(Threadt){
Enumeratione;
intindex;
for(index=0,e=waiters.elements();
e.hasMoreElements();index++){
RWNodenode=(RWNode)e.nextElement();
if(node.t==t)
returnindex;
}
return1;
}
publicRWLock(){
waiters=newVector();
}
publicsynchronizedvoidlockRead(){
RWNodenode;
Threadme=Thread.currentThread();
intindex=getIndex(me);
if(index==1){
node=newRWNode(me,RWNode.READER);
waiters.addElement(node);
}
elsenode=(RWNode)waiters.elementAt(index);
while(getIndex(me)>firstWriter()){
try{
wait();
}catch(Exceptione){}
}
node.nAcquires++;
}
publicsynchronizedvoidlockWrite(){
RWNodenode;
Threadme=Thread.currentThread();
intindex=getIndex(me);
if(index==1){
node=newRWNode(me,RWNode.WRITER);
waiters.addElement(node);
}
else{
node=(RWNode)waiters.elementAt(index);
if(node.state==RWNode.READER)
thrownewIllegalArgumentException("Upgradelock");
node.state=RWNode.WRITER;
}
while(getIndex(me)!=0){
try{
wait();
}catch(Exceptione){}
}
node.nAcquires++;
}
publicsynchronizedvoidunlock(){
RWNodenode;
Threadme=Thread.currentThread();
intindex;
index=getIndex(me);
if(index>firstWriter())
thrownewIllegalArgumentException("Locknotheld");
node=(RWNode)waiters.elementAt(index);
node.nAcquires;
if(node.nAcquires==0){
waiters.removeElementAt(index);
notifyAll();
}
}
}
TheRWLockclassimplementsabasicreaderwriterlock.AswithJava'sReentrantReadWriteLockclass,thisclassisimplementedinawaytoprevent
lockstarvation.
Theinterfacetothereaderwriterlockisverysimple:there'salockRead()methodtoacquirethereadlock,alockWrite()methodtoacquirethewrite
lock,andanunlock()methodtoreleasethelock.(onlyasingleunlock()methodisrequired,forreasonswe'llexploreinamoment).Threadsthatare
attemptingtoacquirethelockareheldinawaitersvector.ThisistoallowtheRWLockclasstoordertherequestsforthepurposeofpreventinglock
starvation.Furthermore,theVectorclassisused,insteadofthemorerecentcontainerclasses,inordertoallowthereaderwriterlocktobeusedwitholder
versionsofJava.
Becauseweneedtokeeptrackofhoweachthreadwantstoacquirethelockwhetheritwantstoacquirethereadlockorthewritelockweneedtocreatea
classtoencapsulatetheinformationofthethreadthatmadetherequestandthetypeofrequestitmade.ThisistheRWNodeclassourwaitersvectorholds
elementsoftypeRWNode.
AcquisitionofthereadlockisdoneinanorderlymannertheRWLockclassdoesn'tjustgrantthereadlockbecauseanotherthreadisalsoholdingtheread
lock.Inordertoobtainthereadlock,athreadthatwantsthewritelockmustnotalreadybeinthequeue.Ifthenodesthatareaheadofthecurrentthreadinthe
waitersqueuewantonlytoacquirethereadlock,wecangoaheadandacquirethelock.Otherwise,wemustwaituntilallofthenodesthatwanttoacquire
thewritelockandareaheadinthewaitervectoracquireandultimatelyfreethelock.
Acquisitionofthewritelockisstricter:wemustbeinpositionzerointhevector.Onlyonethreadmayholdthewritelockatatime.
Thisclassalsosupportsnestedlocks.Thisisaccomplishedbykeepingtrackofthenumberofacquisitionsrequested.Sincethereadlockcanbegrantedto
multiplethreadssimultaneously,wecannolongeruseasimpleinstancevariable(aswedidintheBusyFlagclass)wemustassociatethenAcquires
countwitheachparticularthread.Bothacquisitionmethodsmustchecktoseeifthereisalreadyanodeassociatedwiththecallingthread.
Thisreaderwriterlockclassdoesnothavethenotionof"upgrading"thelockthatis,ifyouholdthereaderlock,youcan'tacquirethewriterlock.Youmust
explicitlyreleasethereaderlockbeforeyouattempttoacquirethewriterlock,oryoureceiveanIllegalArgumentException.Ifanupgradefeaturewere
provided,theclassitselfwouldalsohavetoreleasethereaderlockbeforeacquiringthewriterlock.Atrueupgradeisnotpossibleduetowriterlockrequests
orpossibleupgradesrequestsfromthreadsthatarealsoholdingreaderlocks.
Finally,thereaderwriterlockclasscontainssomemethodstosearchthewaitersvectorforthefirstnodeinthequeuethatrepresentsathreadattemptingto
acquirethewritelock(thefirstWriter()method)andtofindtheindexinthevectorofthenodeassociatedwiththecallingthread(thegetIndex()
method).Wecan'tusetheindexOf()methodoftheVectorclassforthispurposebecausewe'dhavetopasstheindexOf()methodanobjectoftype
[ 1 ]
RWNode,butallwehaveisaThreadobject.
importjava.util.*;
publicclassThreadPool{
classThreadPoolRequest{
Runnabletarget;
Objectlock;
ThreadPoolRequest(Runnablet,Objectl){
target=t;
lock=l;
}
}
classThreadPoolThreadextendsThread{
ThreadPoolparent;
booleanshouldRun=true;
ThreadPoolThread(ThreadPoolparent,inti){
super("ThreadPoolThread"+i);
this.parent=parent;
}
publicvoidrun(){
ThreadPoolRequestobj=null;
while(shouldRun){
try{
parent.cvFlag.getBusyFlag();
while(obj==null&&shouldRun){
try{
obj=(ThreadPoolRequest)
parent.objects.elementAt(0);
parent.objects.removeElementAt(0);
}catch(ArrayIndexOutOfBoundsExceptionaiobe){
obj=null;
}catch(ClassCastExceptioncce){
System.err.println("Unexpecteddata");
obj=null;
}
if(obj==null){
try{
parent.cvAvailable.cvWait();
}catch(InterruptedExceptionie){
return;
}
}
}
}finally{
parent.cvFlag.freeBusyFlag();
}
if(!shouldRun)
return;
obj.target.run();
try{
parent.cvFlag.getBusyFlag();
nObjects;
if(nObjects==0)
parent.cvEmpty.cvSignal();
}finally{
parent.cvFlag.freeBusyFlag();
}
if(obj.lock!=null){
synchronized(obj.lock){
obj.lock.notify();
}
}
obj=null;
}
}
}
Vectorobjects;
intnObjects=0;
CondVarcvAvailable,cvEmpty;
BusyFlagcvFlag;
ThreadPoolThreadpoolThreads[];
booleanterminated=false;
publicThreadPool(intn){
cvFlag=newBusyFlag();
cvAvailable=newCondVar(cvFlag);
cvEmpty=newCondVar(cvFlag);
objects=newVector();
poolThreads=newThreadPoolThread[n];
for(inti=0;i<n;i++){
poolThreads[i]=newThreadPoolThread(this,i);
poolThreads[i].start();
}
}
privatevoidadd(Runnabletarget,Objectlock){
try{
cvFlag.getBusyFlag();
if(terminated)
thrownewIllegalStateException("Threadpoolhasshutdown");
objects.addElement(newThreadPoolRequest(target,lock));
nObjects++;
cvAvailable.cvSignal();
}finally{
cvFlag.freeBusyFlag();
}
}
publicvoidaddRequest(Runnabletarget){
add(target,null);
}
publicvoidaddRequestAndWait(Runnabletarget)
throwsInterruptedException{
Objectlock=newObject();
synchronized(lock){
add(target,lock);
lock.wait();
}
}
publicvoidwaitForAll(booleanterminate)throwsInterruptedException{
try{
cvFlag.getBusyFlag();
while(nObjects!=0)
cvEmpty.cvWait();
if(terminate){
for(inti=0;i<poolThreads.length;i++)
poolThreads[i].shouldRun=false;
cvAvailable.cvBroadcast();
terminated=true;
}
}finally{
cvFlag.freeBusyFlag();
}
}
publicvoidwaitForAll()throwsInterruptedException{
waitForAll(false);
}
}
TheThreadPoolclassimplementsathreadpoolsimilartothethreadpoolexecutordiscussedinChapter10.Theinnerclassinthisexampleperforms
mostofthework.Eachthreadwaitsforworkwhenitissignaled,itsimplypullsthefirstobjectfromthevectorandexecutestheobject.Whenexecutionof
thatobjectisfinished,thethreadmustnotifythelockassociatedwiththeobject(ifany)sothattheaddRequestAndWait()methodknowswhentoreturn
thethreadmustalsonotifythethreadpoolitselfsothatthewaitForAll()methodcheckstoseeifitistimeforittoreturn.
Asaresult,thiscodehasthreewaitingpoints:
Somerequestobjectshaveanassociatedlockobject(theObjectcreatedintheaddRequestAndWait()method).TheaddRequestAndWait()
methodusesthestandardwaitandnotifytechniquetowaitonthisobjectitreceivesnotificationaftertherun()methodhasbeenexecutedbyoneofthe
ThreadPoolThreadobjects.
ACondVarobject(i.e.,aconditionvariable),cvAvailable,isassociatedwiththecvBusyFlag.Thisconditionisusedtosignalthatworkis
availabletobeperformed.WheneverthenObjectsvariableisincremented,workisavailable,sotheadd()methodsignalsathreadthatanewobjectis
available.Similarly,whentherearenoobjectsinthevectortobeprocessed,theThreadPoolThreadobjectswaitonthatconditionvariable.
ACondVarobject,cvEmpty,isalsoassociatedwiththesamecvBusyFlag.Thisconditionisusedtosignalthatallpendingworkhasbeencompleted
thatis,thatthenObjectsvariablehasreachedzero.ThewaitForAll()methodwaitsforthiscondition,whichissignaledbya
ThreadPoolThreadwhenitsetsnObjectstozero.
Weuseconditionvariablesforthelasttwocasesbecausetheysharethesamelock(thecvBusyFlag,whichprotectsaccesstonObjects)eventhoughthey
havedifferentvaluesfortheircondition.Ifwehadusedthestandardwaitandnotifymechanismtosignalthethreadsthatareinterestedinthevalueof
nObjects,wecouldnothavecontrollednotificationaswell:whenevernObjectswassettozero,we'dhavetonotifyallThreadPoolThreadsaswell
asnotifyingthethreadthatisexecutingthewaitForAll()method.
NotethatobjectsthataretoberunbythethreadpoolareexpectedtoimplementtheRunnableinterface.Thisissimilartothethreadpoolexecutor.This
doesn'tmeanthatanewthreadiscreatedforeachtask.Thisinterfaceallowsustotakeexistingcodethatusesthreadsandrunthosetasksviaathreadpool
instead.
Interestinglyenough,thereisnowaytoshutdownathreadpoolautomatically.Ifthethreadpoolobjectweretogooutofscope,itwouldneverbegarbage
collected.Thethreadpoolthreadobjects(likeallthreadobjects)areheldinaninternaldatastructurewithinthevirtualmachine,sotheyarenotgarbage
collecteduntiltheyexit.Andbecausetheyhaveareferencetothethreadpoolitself,thethreadpoolcannotbegarbagecollecteduntilthethreadpoolthreadsare
garbagecollected.Sowehavetohavesomewayofsignalingthethreadpooltoexit:wedothatbypassingatrueparametertothewaitForAll()method.
Then,whenthethreadpoolhasrunallofitsjobs,thewaitForAll()methodarrangesforthethreadpoolthreadstoterminateandmarksthethreadpoolso
thatnomorejobscanbeaddedtoit.Thethreadpoolthreadsthenexit,andthethreadpoolcanbegarbagecollected.
importjava.util.*;
publicclassJobSchedulerimplementsRunnable{
finalpublicstaticintONCE=1;
finalpublicstaticintFOREVER=1;
finalpublicstaticlongHOURLY=(long)60*60*1000;
finalpublicstaticlongDAILY=24*HOURLY;
finalpublicstaticlongWEEKLY=7*DAILY;
finalpublicstaticlongMONTHLY=1;
finalpublicstaticlongYEARLY=2;
privateclassJobNode{
publicRunnablejob;
publicDateexecuteAt;
publiclonginterval;
publicintcount;
}
privateThreadPooltp;
privateDaemonLockdlock=newDaemonLock();
privateVectorjobs=newVector(100);
publicJobScheduler(intpoolSize){
tp=(poolSize>0)?newThreadPool(poolSize):null;
Threadjs=newThread(this);
js.setDaemon(true);
js.start();
}
privatesynchronizedvoidaddJob(JobNodejob){
dlock.acquire();
jobs.addElement(job);
notify();
}
privatesynchronizedvoiddeleteJob(Runnablejob){
for(inti=0;i<jobs.size();i++){
if(((JobNode)jobs.elementAt(i)).job==job){
jobs.removeElementAt(i);
dlock.release();
break;
}
}
}
privateJobNodeupdateJobNode(JobNodejn){
Calendarcal=Calendar.getInstance();
cal.setTime(jn.executeAt);
if(jn.interval==MONTHLY){
//Thereisaminorbug.(seejava.util.calendar)
cal.add(Calendar.MONTH,1);
jn.executeAt=cal.getTime();
}elseif(jn.interval==YEARLY){
cal.add(Calendar.YEAR,1);
jn.executeAt=cal.getTime();
}else{
jn.executeAt=newDate(jn.executeAt.getTime()+jn.interval);
}
jn.count=(jn.count==FOREVER)?FOREVER:jn.count1;
return(jn.count!=0)?jn:null;
}
privatesynchronizedlongrunJobs(){
longminDiff=Long.MAX_VALUE;
longnow=System.currentTimeMillis();
for(inti=0;i<jobs.size();){
JobNodejn=(JobNode)jobs.elementAt(i);
if(jn.executeAt.getTime()<=now){
if(tp!=null){
tp.addRequest(jn.job);
}else{
Threadjt=newThread(jn.job);
jt.setDaemon(false);
jt.start();
}
if(updateJobNode(jn)==null){
jobs.removeElementAt(i);
dlock.release();
}
}else{
longdiff=jn.executeAt.getTime()now;
minDiff=Math.min(diff,minDiff);
i++;
}
}
returnminDiff;
}
publicsynchronizedvoidrun(){
while(true){
longwaitTime=runJobs();
try{
wait(waitTime);
}catch(Exceptione){};
}
}
publicvoidexecute(Runnablejob){
executeIn(job,(long)0);
}
publicvoidexecuteIn(Runnablejob,longmillis){
executeInAndRepeat(job,millis,1000,ONCE);
}
publicvoidexecuteInAndRepeat(Runnablejob,longmillis,longrepeat){
executeInAndRepeat(job,millis,repeat,FOREVER);
}
publicvoidexecuteInAndRepeat(Runnablejob,longmillis,
longrepeat,intcount){
Datewhen=newDate(System.currentTimeMillis()+millis);
executeAtAndRepeat(job,when,repeat,count);
}
publicvoidexecuteAt(Runnablejob,Datewhen){
executeAtAndRepeat(job,when,1000,ONCE);
}
publicvoidexecuteAtAndRepeat(Runnablejob,Datewhen,longrepeat){
executeAtAndRepeat(job,when,repeat,FOREVER);
}
publicvoidexecuteAtAndRepeat(Runnablejob,Datewhen,
longrepeat,intcount){
JobNodejn=newJobNode();
jn.job=job;
jn.executeAt=when;
jn.interval=repeat;
jn.count=count;
addJob(jn);
}
publicvoidcancel(Runnablejob){
deleteJob(job);
}
}
TheJobSchedulerclassimplementsatimebasedexecutionsystemsimilartothescheduledexecutordiscussedinChapter11.Likethe
ScheduledThreadPoolExecutorclass,thisclassalsousesathreadpoolinternally,allowingthetaskstoexecuteintheseparatethreadswithinthepool.
However,thisclassalsoprovidestheoptionnottouseathreadpool,meaningthatseparatethreadsarestartedforeveryjob.Thisoptionisusefulifthejobis
alongtermtaskorforajobthatrunsinthebackgroundindefinitely.Assumingthatthethreadingsystemdoesn'tgetoverloaded,thisalsoallowsthejobsto
beexecutedasclosetotherequestedtimeaspossible.
Theclassisdesignedtobeassimpleandasbasicaspossible:theclassjustiteratesovertherequestedjobs(theelementsinthejobsvector)andeither
addsthejobsthatneedtobeexecutedtoathreadpoolforprocessingorstartsanewthreadtoexecutethejob.Inaddition,weneedtofindthetimeforthejob
thatisduetorunnext,andwaitforthistimetooccur.Theentireprocessisthenrepeated.
Forcompleteness,we'veaddedalittlecomplexityinourJobSchedulerclass.Inadditiontoacceptingarunnableobjectthatcanbeexecutedandatimeat
whichtoperformthejob,wealsoacceptacountofthenumberoftimesthejobistobeperformedandthetimetowaitbetweenexecutionsofthejob.
Consequently,afterajobisexecuted,weneedtocalculatewhetheranotheriterationisnecessaryandwhentoperformthisiteration.
InourJobSchedulerclass,thisisallhandledbyasinglethreadthatcallstherunJobs()method.Thetaskofdecidingwhetherthejobneedstobe
executedagainisdonebytheupdateJobNode()methodaddingjobstoanddeletingjobsfromtherequestedjobsvectorisaccomplishedbythe
addJob()anddeleteJob()methods,respectively.MostofthelogicfortheJobSchedulerclassisactuallytheimplementationofthemanyoptions
andmethodsintheinterfaceprovidedforthedeveloper.
OurJobSchedulerclassprovideseightmethods:
publicvoidexecute(Runnablejob)
Usedforajobthatistobeexecutedoncesimplyrunsthejob.
publicvoidexecuteIn(Runnablejob,longmillis)
Usedforajobthatistobeexecutedoncerunsthejobafterthespecifiednumberofmillisecondshaselapsed.
publicvoidexecuteAt(Runnablejob,Datewhen)
Usedforajobthatistobeexecutedoncerunsthejobatthetimespecified.
publicvoidexecuteInAndRepeat(Runnablejob,longmillis,longrepeat)
publicvoidexecuteInAndRepeat(Runnablejob,longmillis,longrepeat,intcount)
publicvoidexecuteAtAndRepeat(Runnablejob,Datewhen,longrepeat)
publicvoidexecuteAtAndRepeat(Runnablejob,Datewhen,longrepeat,intcount)
Usedforrepeatingjobs.Thesemethodsrunthejobafterthenumberofmillisecondsspecifiedbythemillisparameterhaselapsed(oratthetime
specifiedbythewhenparameter).Theyrunthejobagainafterthenumberofmillisecondsspecifiedbytherepeatparameterhaselapsed.Thisprocess
isrepeatedasspecifiedbythecountparameter.Ifnocountisspecified,thejobisrepeatedforever.
TheconstantsHOURLY,DAILY,WEEKLY,MONTHLY,andYEARLYmayalsobepassedastherepeatparameter.TheHOURLY,DAILY,andWEEKLY
parametersareprovidedforconvenience.However,theMONTHLYandYEARLYparametersareprocesseddifferentlybythejobschedulersincethe
schedulerhastotakeintoaccountthedifferentnumberofdaysinthemonthandtheleapyear.
publicvoidcancel(Runnablejob)
Cancelsthespecifiedjob.Noerrorisgeneratedifthejobisnotintherequestedjobsvectorsinceitispossiblethatthejobhasexecutedandbeenremoved
fromthevectorbeforethecancel()methodiscalled.Ifthesamejobisplacedonthelistmorethanonce,thismethodremovesthefirstjobthatitfinds
onthelist.
Asrichasthesetofmethodsprovidedbythisclass,itcanbeconsideredweakinfeaturesbythosewhohaveusedjobschedulersprovidedbysomeoperating
systems.Inthosesystems,developerscanspecifycriteriasuchasdayoftheweek,dayofthemonth,weekoftheyear,andsoon.Comparedtothe
ScheduledThreadPoolExecutorclass,itisalsomissingsomeofthecontrolfeaturesforrepeatingjobs.
Weaccomplishthisbymakingthethreadsinthejobschedulerdaemonthreadsthatwaytheyexitwhennomoreuserthreadsareactive.TheDaemonLock
classprotectsagainstprematureexit:itmakessurethatoneuserthreadisactiveaslongasthejobschedulerhastaskstorun.
NotethattheScheduledThreadPoolExecutorclassdoesn'tneedtousesomethinglikethisclasssinceitsshutdown()methodaccomplishesa
gracefulshutdown.
TheDaemonLockclasslookslikethis:
packagejavathreads.examples.appa;
publicclassDaemonLockimplementsRunnable{
privateintlockCount=0;
publicsynchronizedvoidacquire(){
if(lockCount++==0){
Threadt=newThread(this);
t.setDaemon(false);
t.start();
}
}
publicsynchronizedvoidrelease(){
if(lockCount==0){
notify();
}
}
publicsynchronizedvoidrun(){
while(lockCount!=0){
try{
wait();
}catch(InterruptedExceptionex){};
}
}
}
Summary
Inaway,thisappendixislikeahistorylesson:wehavejustreviewedthemajorclassesdevelopedinthepreviouseditionsofthisbook.Theseclasseshave
beensupercededbytheadditionsinJ2SE5.0.WhiletheenhancementsinJ2SE5.0provideproductionqualitysupport,theyalsomakeitmoredifficultfor
readers.Thenewclassesaredesignedtobeused,nottobeeducationaltoolstherefore,theircodeiswrittenoptimallyratherthansimply.
Byreviewingthesesupercededclasses,weaccomplishtwotasks.Weprovideedificationbyshowingclassesthataresimplertounderstand.Wealsoprovide
toolsthatcanbeusedbydeveloperswhohavenotyetupgradedtoJ2SE5.0.Forthosedevelopers,theseclasses,availableintheonlinesourceforthisbook,
couldbeusedintheinterim.
[ 1 ]
InJ2SE5.0,that'snolongeraproblem,sincetheVectorclasssupportsintrinsics.ButinJ2SE5.0,you'llbeusingthe
ReadWriteLockclassanyway.
Index
ANO TEO NTHEDIG ITAL INDEX
Alinkinanindexentryisdisplayedasthesectiontitleinwhichthatentryappears.Becausesomesectionshavemultipleindexmarkers,itisnot
unusualforanentrytohaveseverallinkstothesamesection.Clickingonanylinkwilltakeyoudirectlytotheplaceinthetextinwhichthemarker
appears.
A
accept()method,ScalingUsingTraditionalI/O
access,WhatIsaThread?,SwingThreadingRestrictions,ProcessingontheEventDispatchingThread,SingleThreadedAccess,ThreadsandJavaSecurity
checkAccess()method,ThreadsandJavaSecurity
heaps,WhatIsaThread?
pools,SingleThreadedAccess
Swingobjects,SwingThreadingRestrictions,ProcessingontheEventDispatchingThread
acquiringlocks,LockStarvation
ActionListenerinterface,Thejavax.swing.TimerClass
actionPerformed()method,TheThreadClass,LongRunningEventCallbacks
addActionListener()method,Thejavax.swing.TimerClass
advancedatomicdatatypes,Advancedatomicdatatypes
alarms,AlarmsandTimers
algorithms,ParallelizableAlgorithms,UsingtheAtomicClasses,Changingalgorithms,UsingtheCollectionClasses
collectionclasses,UsingtheCollectionClasses
modification,Changingalgorithms
parallelizable,ParallelizableAlgorithms
synchronization,UsingtheAtomicClasses
Amdahl'sLaw,MultiprocessorScaling
analysisofloops,LoopAnalysisandTransformations
Ant,CompilingandRunningtheExamples
APIs(applicationprogramminginterfaces),What'sNewinThisEdition?,What'sNewinThisEdition?
(seealsointerfaces)
JSR166,What'sNewinThisEdition?
applets,JavaTerms
applicationprogramminginterfaces,What'sNewinThisEdition?(seeAPIs)
applications,JavaTerms,WhatIsaThread?WhatIsaThread?,TheThreadClass,TheThreadClass
compiling,TheThreadClass
running,TheThreadClass
tasks,WhatIsaThread?WhatIsaThread?
architectureexamples,TheExampleArchitectureTheExampleArchitecture
ArrayBlockingQueue,QueuesandSizes
arrays,TheVolatileKeyword,OverviewoftheAtomicClasses,ParallelizingaSingleThreadedProgram
atomic,OverviewoftheAtomicClasses
lookuptable,ParallelizingaSingleThreadedProgram
volatilekeyword,TheVolatileKeyword
asynchronousbehavior,WhyThreads?
atomicarrays,OverviewoftheAtomicClasses
atomiccode,TheSynchronizedKeyword
atomicdatatypes,Advancedatomicdatatypes
atomicvariables,AtomicVariablesBulkdatamodification,Variablesubstitution,NotificationsandAtomicVariables,Dataexchange,AtomicVariablesand
ContendedSynchronization
dataexchange,Dataexchange
notification,NotificationsandAtomicVariables
performance,AtomicVariablesandContendedSynchronization
substitution,Variablesubstitution
AtomicDoubleclass,Advancedatomicdatatypes,Bulkdatamodification,Bulkdatamodification,Reductionvariables
AtomicInteger.getAndIncrement()method,AtomicVariablesandContendedSynchronization
AtomicIntegerArrayclass,OverviewoftheAtomicClasses,OverviewoftheAtomicClasses
AtomicIntegerFieldUpdaterclass,OverviewoftheAtomicClasses
AtomicLongArrayclass,OverviewoftheAtomicClasses
AtomicLongFieldUpdaterclass,OverviewoftheAtomicClasses
AtomicMarkableReferenceclass,OverviewoftheAtomicClasses
AtomicReferenceArrayclass,OverviewoftheAtomicClasses
AtomicReferenceFieldUpdaterclass,OverviewoftheAtomicClasses
AtomicStampedReferenceclass,OverviewoftheAtomicClasses
autoparallelization,ParallelizingaSingleThreadedProgram
automaticlockreleases,DeadlockandAutomaticLockReleases
await()method,ConditionVariablesConditionVariables,BarrierCountdownLatch,DeadlockDetection,DeadlockDetection,DeadlockDetection,
OverviewofTaskScheduling
deadlock,DeadlockDetection
awaitTerminated()method,Executors
B
Barrierclass,TheBarrierClass
barriers,SynchronizationTerms,Barrier
behaviorofunsynchronizedmethods,MoreonRaceConditions
blocks,NonblockingI/O,SynchronizedBlocks,SynchronizedBlocks,WaitandNotifyMechanismwithSynchronizedBlocks,TheEffectofReordering
Statements,SynchronizationTerms,SynchronizationTerms,LockStarvation,TheSchedulingProcess,NonblockingI/O
criticalsections,SynchronizationTerms
I/O,NonblockingI/O,NonblockingI/O
locks,SynchronizationTerms
scheduling,TheSchedulingProcess
synchronization,WaitandNotifyMechanismwithSynchronizedBlocks,TheEffectofReorderingStatements,LockStarvation
synchronizedkeyword,SynchronizedBlocks
synchronizedmechanisms,SynchronizedBlocks
bottlenecks,OverviewofPerformance
browsers,JavaTerms
bulkdatamodification,Bulkdatamodification,Bulkdatamodification
BusyFlagclass,TheBusyFlagClass
C
caches,ThreadLocalVariables
calculationofraceconditions,MoreonRaceConditionsMoreonRaceConditions
callabletasks,pools,CallableTasksandFutureResultsTheFutureTaskClass
callbacks,NestedLocks,LongRunningEventCallbacksLongRunningEventCallbacks
events,LongRunningEventCallbacksLongRunningEventCallbacks
cancel()method,Thejava.util.TimerClass
canThreadWaitOnLock()method,DeadlockDetection
characters,TheExampleArchitecture
checkAccess()method,ThreadsandJavaSecurity
childValue()method,InheritableThreadLocalVariables
chunkscheduling,Staticorchunkscheduling
classes,TheExampleArchitectureTheExampleArchitecture,TheExampleArchitecture,TheThreadClassTheThreadClass,TheThreadClass,The
LifecycleofaThreadThreadCleanup,TheRunnableInterfaceTheRunnableInterface,ExplicitLocking,LockScopeSynchronizedBlocks,Nested
Locks,WaitandNotify,AtomicVariablesBulkdatamodification,SynchronizationTerms,SynchronizationClassesAddedinJ2SE5.0Reader/Writer
Locks,ThreadsandCollectionClasses,ThreadsafeCollectionClasses,ThreadAwareClasses
atomic,AtomicVariablesBulkdatamodification
collection,ThreadsandCollectionClasses(seecollectionclasses)
helper,TheExampleArchitecture
J2SE5.0,SynchronizationClassesAddedinJ2SE5.0Reader/WriterLocks
Object,waitandnotifymechanism,WaitandNotify
RandomCharacterGenerator,TheThreadClass
ReentrantLock,NestedLocks
ScoreLabel,ExplicitLocking,LockScopeSynchronizedBlocks
modifying,LockScopeSynchronizedBlocks
semaphores,SynchronizationTerms
Thread,TheThreadClassTheThreadClass,TheLifecycleofaThreadThreadCleanup,TheRunnableInterfaceTheRunnableInterface
lifecycles,TheLifecycleofaThreadThreadCleanup
Runnableinterface,TheRunnableInterfaceTheRunnableInterface
threadaware,ThreadAwareClasses
threadsafe,ThreadsafeCollectionClasses
utility,TheExampleArchitectureTheExampleArchitecture
classifications,variables,VariableClassifications
classloaderobject,ThreadsandClassLoadingThreadsandClassLoading
cleanup,threads,ThreadCleanup
clients,ATraditionalI/OServer
tracking,ATraditionalI/OServer
code,JavaTerms,JavaVersions,Tools,andCode,AbouttheExamplesCompilingandRunningtheExamples,TheExampleArchitectureTheExample
Architecture,TheSynchronizedKeyword,SynchronizedBlocks,Deadlock,AtomicVariablesBulkdatamodification,ProcessingontheEventDispatching
Thread,ATraditionalI/OServerScalingUsingTraditionalI/O,ASingleThreadedNIOServer,ThreadsandJavaSecurity,LoopAnalysisand
Transformations
architectureexamples,TheExampleArchitectureTheExampleArchitecture
atomic,TheSynchronizedKeyword
atomicclasses,AtomicVariablesBulkdatamodification
blocks,SynchronizedBlocks
deadlock,Deadlock
examplesof,AbouttheExamplesCompilingandRunningtheExamples
I/Oservers,ATraditionalI/OServerScalingUsingTraditionalI/O
permissions,ThreadsandJavaSecurity
subclasses,ASingleThreadedNIOServer
Swingobjectaccess,ProcessingontheEventDispatchingThread
transformations,LoopAnalysisandTransformations
collectionclasses,OverviewofCollectionClasses,CollectionInterfacesSynchronizationandCollectionClasses,SynchronizationandCollection
ClassesThreadAwareClasses,TheProducer/ConsumerPatternTheProducer/ConsumerPattern,UsingtheCollectionClasses,SynchronizedCollections
applying,UsingtheCollectionClasses
interfaces,CollectionInterfacesSynchronizationandCollectionClasses
producer/consumerpattern,TheProducer/ConsumerPatternTheProducer/ConsumerPattern
synchronization,SynchronizationandCollectionClassesThreadAwareClasses,SynchronizedCollections
Collections.synchronizedCollection()method,SynchronizedCollections
commands,execution,WhatIsaThread?
compareAndSet()method,OverviewoftheAtomicClasses,Compareandset,Advancedatomicdatatypes
compilers,autoparallelization,ParallelizingaSingleThreadedProgram
compiling,CompilingandRunningtheExamples,TheThreadClass
applications,TheThreadClass
code,CompilingandRunningtheExamples
complexpriorities,Complexpriorities
components,JavaTerms,ProcessingontheEventDispatchingThread
concurrencyutilities,What'sNewinThisEdition?,JavaTerms
ConcurrentHashMapclass,TheConcurrentHashMapClass
Conditioninterface,ConditionVariables
Conditionobjects,creating,ConditionVariables
conditionvariables,ConditionVariablesConditionVariables,SynchronizationTerms,DeadlockDetection
deadlock,DeadlockDetection
conditions,TheWaitandNotifyMechanismandSynchronization,TheWaitandNotifyMechanismandSynchronization
(seealsoracecondition)
notification,TheWaitandNotifyMechanismandSynchronization
CondVarclass,TheCondVarClass
containers,JavaTerms
contendedlocks,CanYouAvoidSynchronization?
contendedsynchronization,AtomicVariablesandContendedSynchronization
contextclassloaders,ThreadsandClassLoading
copyonwriteoperations,UsingtheCollectionClasses
CopyOnWriteArrayListclass,IteratorsandEnumerations
CopyOnWriteArraySetclass,IteratorsandEnumerations
countdownlatches,CountdownLatch
CountDownLatchclass,CountdownLatch
countStackFrames()method,StackAPIs
criticalsections,SynchronizationTerms
crosscallingmethods,NestedLocks
currentThread()method,DeterminingtheCurrentThread
CyclicBarrierclass,Barrier
D
DaemonLockclass,TheDaemonLockClass
daemons,DaemonThreads
dataexchange,atomicvariables,Dataexchange
datasharing,WhatIsaThread?
datasockets,ATraditionalI/OServer
datatypes,advancedatomic,Advancedatomicdatatypes
deadlock,NestedLocks,Deadlock,PreventingDeadlockPreventingDeadlockwithTimeouts,DeadlockDetectionDeadlockDetection,DeadlockDetection,
DeadlockDetection,DeadlockDetection,DeadlockDetection,DeadlockDetection,DeadlockDetection
await()method,DeadlockDetection
checks,DeadlockDetection
conditionvariables,DeadlockDetection
detectionof,DeadlockDetectionDeadlockDetection
exceptions,DeadlockDetection
loops,DeadlockDetection
preventing,PreventingDeadlockPreventingDeadlockwithTimeouts
searching,DeadlockDetection
DeadlockDetectedException,DeadlockDetection
DeadlockDetectingLockclass,DeadlockDetection
declarationoflocks,LockFairness
delays,InterruptingaThread
design,doublecheckedlocking,DoubleCheckedLocking
detectionofdeadlock,PreventingDeadlock,DeadlockDetectionDeadlockDetection
distribution,Loopdistribution
doneflag,TheVolatileKeyword
Doubleclass,Advancedatomicdatatypes
doublecheckedlocking,DoubleCheckedLocking
E
EJBs(EnterpriseJavaBeans),JavaTerms
EnterpriseJavaBeans(EJBs),JavaTerms
enumerations,IteratorsandEnumerations
errors,Changingalgorithms,DeadlockDetection,StackOverflowErrors,OutofMemoryErrors
deadlock,DeadlockDetection
outofmemory,OutofMemoryErrors
stackoverflow,StackOverflowErrors
synchronization,Changingalgorithms
eventdispatchingthread,ThreadsandObjects,SwingThreadingRestrictionsSwingThreadingRestrictions
events,TheExampleArchitecture,TheLifecycleofaThreadThreadCleanup,Retryingoperations,SynchronizationTerms,ProcessingontheEvent
DispatchingThread,LongRunningEventCallbacksLongRunningEventCallbacks
callbacks,LongRunningEventCallbacksLongRunningEventCallbacks
lifecycles,TheLifecycleofaThreadThreadCleanup
processing,Retryingoperations,ProcessingontheEventDispatchingThread
variables,SynchronizationTerms
exceptions,DeadlockDetection,SwingThreadingRestrictions,PriorityExceptions,RejectedTasks,ThreadsandExceptionHandlingTheThreadDeath
Class
deadlock,DeadlockDetection
pools,RejectedTasks
priority,PriorityExceptions
Swingobjects,SwingThreadingRestrictions
exchange()method,Exchanger
exchangers,Exchanger
execute()method,Executors
execution,WhatIsaThread?,WhatIsaThread?,TheThreadClass,MoreonRaceConditions,TheEffectofReorderingStatements,Thejava.util.Timer
Class,TheScheduledThreadPoolExecutorClass
java.util.Timerclass,Thejava.util.TimerClass,TheScheduledThreadPoolExecutorClass
programs,WhatIsaThread?
racecondition,MoreonRaceConditions
statements,TheEffectofReorderingStatements
threadclass,TheThreadClass
virtualmachine,WhatIsaThread?
executors,pools,ExecutorsExecutors
exitingstate,scheduling,TheSchedulingProcess
explicitlocks,ExplicitLockingExplicitLocking,Deadlock,PreventingDeadlockwithTimeouts
deadlock,PreventingDeadlockwithTimeouts
F
fairness,locks,LockFairnessLockFairness
firingevents,TheExampleArchitecture
flags,SettingaFlag,InterruptingaThread,TheVolatileKeyword,CallableTasksandFutureResults,TheBusyFlagClass
BusyFlagclass,TheBusyFlagClass
done,TheVolatileKeyword
mayInterruptIfRunning,CallableTasksandFutureResults
queries,InterruptingaThread
setting,SettingaFlag
floatingpointvalues,Advancedatomicdatatypes
floatingpointvariables,OverviewoftheAtomicClasses
freeIfHardwait()method,DeadlockDetection
functionalityofconditionvariables,NotificationsandAtomicVariables
Futureinterface,CallableTasksandFutureResults,UsingtheFutureInterface
futureresults,pools,CallableTasksandFutureResultsTheFutureTaskClass
G
garbagecollection,WhatIsaThread?,ThreadCleanup,ThreadUnsafeCollectionClasses,TheSchedulingProcess,Thejava.util.TimerClass,Thread
Groups,DaemonThreads,MeasuringJavaPerformance,ParallelizingaSingleThreadedProgram,TheThreadPoolClassTheThreadPoolClass
genericNIOservers,ASingleThreadedNIOServer
get()method,OverviewoftheAtomicClasses
getAllLocksOwned()method,DeadlockDetection
getAllStackTraces()method,StackAPIs
getAllThreadsHardwaiting()method,DeadlockDetection
getAndSet()method,OverviewoftheAtomicClasses,Retryingoperations
getContextClassLoader()method,ThreadsandClassLoading
getDelay()method,Thejavax.swing.TimerClass
getInitialDelay()method,Thejavax.swing.TimerClass
getListeners()method,Thejavax.swing.TimerClass
getLogTimers()method,Thejavax.swing.TimerClass
getStackTrace()method,StackAPIs
getter/setterpattern,ThreadLocalVariables
getThreadGroup()method,ThreadGroups
GET_STRING_REQUESTmessage,AnExampleMultithreadedServer
GET_STRING_RESPONSEmessage,AnExampleMultithreadedServer,ASingleThreadedNIOServer
greenthreads,GreenThreads
groups,TheThreadClass,ThreadGroupsThreadGroups
guidedselfscheduling,Guidedselfscheduling
H
handleClient()method,ASingleThreadedNIOServer
handleServer()method,ASingleThreadedNIOServer
hardwaitinglists,DeadlockDetection
Hashtableclass,IteratorsandEnumerations
hashtables,TheConcurrentHashMapClass
heaps,WhatIsaThread?
helperclasses,TheExampleArchitecture
hierarchies,ThreadGroups
I/O,WhyThreads?,NonblockingI/O,NonblockingI/O,ATraditionalI/OServerScalingUsingTraditionalI/O,ANewI/OServerAMultithreadedNew
I/OServer,InterruptedI/OInterruptedI/O
asynchronousbehavior,WhyThreads?
multiplexing,NonblockingI/O
nonblocking,NonblockingI/O
servers,ATraditionalI/OServerScalingUsingTraditionalI/O,ANewI/OServerAMultithreadedNewI/OServer,InterruptedI/OInterruptedI/O
interrupted,InterruptedI/OInterruptedI/O
JDK1.4,ANewI/OServerAMultithreadedNewI/OServer
implementations,PopularThreadingImplementationsLinuxNativeThreads,AnExampleMultithreadedServer
scheduling,PopularThreadingImplementationsLinuxNativeThreads
TCPServerclass,AnExampleMultithreadedServer
independenttasks,IndependentTasks
InheritableThreadLocalclass,InheritableThreadLocalVariables
initialstate,scheduling,TheSchedulingProcess
initializationofbarriers,Barrier
initialValue()method,ThreadLocalVariables
innerloops,InnerLoopThreading,InnerLoopThreading,ASmallInnerLoopTest
testing,ASmallInnerLoopTest
interaction,ThreadsandObjects
interchanges,loops,Loopinterchange
interfaces,CreatingaThread,TheRunnableInterfaceTheRunnableInterface,ExplicitLockingExplicitLocking,TheLockInterface,ConditionVariables,
Semaphore,Barrier,CountdownLatch,Exchanger,Reader/WriterLocks,DeadlockandAutomaticLockReleases,CollectionInterfacesSynchronization
andCollectionClasses,ExecutorsExecutors,RejectedTasks,CallableTasksandFutureResults,Thejava.util.TimerClass,Thejavax.swing.TimerClass,
Thejavax.swing.TimerClass,UsingtheFutureInterface,StackAPIs
ActionListener,Thejavax.swing.TimerClass
barriers,Barrier
collectionclasses,CollectionInterfacesSynchronizationandCollectionClasses
Condition,ConditionVariables
countdownlatches,CountdownLatch
exchangers,Exchanger
executors,ExecutorsExecutors
Future,CallableTasksandFutureResults,UsingtheFutureInterface
javax.swing.Timerclass,Thejavax.swing.TimerClass
Lock,ExplicitLockingExplicitLocking,DeadlockandAutomaticLockReleases
lock,TheLockInterface
locks,Reader/WriterLocks
RejectedExecutionHandler,RejectedTasks
Runnable,CreatingaThread,TheRunnableInterfaceTheRunnableInterface
semaphores,Semaphore
stacks,StackAPIs
Timerclass,Thejava.util.TimerClass
InternetExplorer,JavaTerms
interpreters,JavaTerms
interrupt()method,InterruptedI/O,ThreadGroups
interruptedI/Oservers,InterruptedI/OInterruptedI/O
interruptiblelockingrequests,deadlockdetection,DeadlockDetection
interruptingthreads,InterruptingaThread
inversion,Priorityinversion
invokeAll()methods,Executors
invokeAndWait()method,UsinginvokeLater()andinvokeAndWait()
invokeAny()methods,Executors
invokeLater()method,UsinginvokeLater()andinvokeAndWait()
isAlive()method,StartingaThread
isCoalesce()method,Thejavax.swing.TimerClass
isEventDispatchThread()method,LongRunningEventCallbacks
isolation,loops,Loopisolation
isRepeats()method,Thejavax.swing.TimerClass
isRunning()method,Thejavax.swing.TimerClass
isTerminated()method,Executors
iteration,loops,ParallelizingaSingleThreadedProgram
iterators,IteratorsandEnumerations
J
J2EE(Java2EnterpriseEdition),JavaTerms
J2SE5.0,What'sNewinThisEdition?,JavaTermsJavaVersions,Tools,andCode,TheLockInterface,UsingtheAtomicClasses,Synchronization
ClassesAddedinJ2SE5.0Reader/WriterLocks,SolarisNativeThreads
categoriesoffeaturesaddedto,What'sNewinThisEdition?
classes,SynchronizationClassesAddedinJ2SE5.0Reader/WriterLocks
Java,JavaTerms
Java2EnterpriseEdition(J2EE),JavaTerms
JavaSpecificationRequest(JSR),What'sNewinThisEdition?
JavaThreadclass,SchedulingwithThreadPriorities
java.lang.SecurityManagerclass,ThreadsandJavaSecurityThreadsandJavaSecurity
java.lang.ThreadGroupclass,ThreadGroupsThreadGroups
java.lang.ThreadLocalclass,ThreadLocalVariables
java.util.Timerclass,Thejava.util.TimerClassUsingtheTimer
javax.swing.Timerclass,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass
JDK1.4,I/Oservers,ANewI/OServerAMultithreadedNewI/OServer
JobSchedulerclass,TheJobSchedulerClass
join()method,ThreadCleanup,OverviewofTaskScheduling
JSR(JavaSpecificationRequest),What'sNewinThisEdition?
K
keywords,What'sNewinThisEdition?,TheExampleArchitecture,SettingaFlag,TheSynchronizedKeywordTheSynchronizedKeyword,TheVolatile
KeywordTheVolatileKeyword,TheVolatileKeyword,ExplicitLocking,ExplicitLocking,SynchronizedBlocks,SynchronizedBlocksChoosinga
LockingMechanism,TheLockInterface,Summary,CanYouAvoidSynchronization?,TheEffectofRegisters,PreventingDeadlockDeadlockand
AutomaticLockReleases,DeadlockDetection
blocks,SynchronizedBlocks
synchronized,What'sNewinThisEdition?,TheExampleArchitecture,TheSynchronizedKeywordTheSynchronizedKeyword,TheVolatile
Keyword,ExplicitLocking,ExplicitLocking,SynchronizedBlocksChoosingaLockingMechanism,TheLockInterface,Preventing
DeadlockDeadlockandAutomaticLockReleases,DeadlockDetection
volatile,SettingaFlag,TheVolatileKeywordTheVolatileKeyword,Summary,CanYouAvoidSynchronization?,TheEffectofRegisters
L
LAN(localareanetwork),NonblockingI/O
lifecycles,TheLifecycleofaThreadThreadCleanup
lightweightprocesses(LWPs),SolarisNativeThreads
LinkedBlockingQueue,QueuesandSizes
Linuxnativethreads,LinuxNativeThreads
listeners.toArray()method,ComplexSynchronization
loadbalancing,LoopSchedulingandLoadBalancing
loadingclasses,ThreadsandClassLoadingThreadsandClassLoading
localareanetwork(LAN),NonblockingI/O
locations,memory,TheEffectofRegisters
Lockinterface,ExplicitLockingExplicitLocking,TheLockInterface,DeadlockandAutomaticLockReleases
deadlock,DeadlockandAutomaticLockReleases
Lockobject,ConditionVariables
lock()method,ExplicitLocking,DeadlockDetection
locks,TheSynchronizedKeyword,TheVolatileKeyword,ExplicitLockingExplicitLocking,LockScopeSynchronizedBlocks,ChoosingaLocking
MechanismTheLockInterface,NestedLocksNestedLocks,Deadlock,Deadlock,LockFairnessLockFairness,CanYouAvoid
Synchronization?DoubleCheckedLocking,DoubleCheckedLocking,SynchronizationTerms,SynchronizationTerms,SynchronizationTerms,
SynchronizationTerms,SynchronizationTerms,Semaphore,Reader/WriterLocks,Reader/WriterLocks,Reader/WriterLocks,Preventing
DeadlockPreventingDeadlockwithTimeouts,PreventingDeadlock,DeadlockandAutomaticLockReleases,DeadlockDetectionDeadlockDetection,
DeadlockDetection,LockStarvationLockStarvationandReader/WriterLocks
automaticreleases,DeadlockandAutomaticLockReleases
conditionvariables,SynchronizationTerms
deadlock,Deadlock,PreventingDeadlockPreventingDeadlockwithTimeouts,DeadlockDetectionDeadlockDetection
detectionof,DeadlockDetectionDeadlockDetection
doublecheckedlocking,DoubleCheckedLocking
explicit,Deadlock
explicitlocking,ExplicitLockingExplicitLocking
fairness,LockFairnessLockFairness
interfaces,Reader/WriterLocks
multipleobjects,PreventingDeadlock
mutex,TheSynchronizedKeyword
nested,NestedLocksNestedLocks
reader,SynchronizationTerms,Reader/WriterLocks
scope,TheVolatileKeyword,LockScopeSynchronizedBlocks
selecting,ChoosingaLockingMechanismTheLockInterface
semaphores,SynchronizationTerms,Semaphore
starvation,LockStarvationLockStarvationandReader/WriterLocks
synchronization,CanYouAvoidSynchronization?DoubleCheckedLocking
trees,DeadlockDetection
writer,SynchronizationTerms,Reader/WriterLocks
longrunningeventcallbacks,LongRunningEventCallbacksLongRunningEventCallbacks
lookupTablearrays,ParallelizingaSingleThreadedProgram
loopprivatevariables,Loopprivatevariables
loopDoRange()method,ParallelizingaSingleThreadedProgram,ParallelizingaSingleThreadedProgram
loopGetRange()method,ParallelizingaSingleThreadedProgram
LoopHandlerclass,ParallelizingaSingleThreadedProgram,InnerLoopThreading
LoopPrinterclass,LoopPrinting,APrintingTest
loopProcess()method,ParallelizingaSingleThreadedProgram
loops,ParallelizableAlgorithms,Retryingoperations,DeadlockDetection,DeadlockDetection,LockStarvation,ParallelizingLoopsforMultiprocessor
MachinesExampleClasses,ParallelizingaSingleThreadedProgram,ParallelizingaSingleThreadedProgram,LoopSchedulingandLoadBalancing,
Reductionvariables,LoopAnalysisandTransformations,LoopAnalysisandTransformations,Loopdistribution,Loopisolation,Loopinterchange,Loop
reimplementation,InnerLoopThreading,InnerLoopThreading,LoopPrinting,MultiprocessorScaling,ASimpleLoopTest,ASmallInnerLoopTest,A
PrintingTest
analysis,LoopAnalysisandTransformations
deadlock,DeadlockDetection
distribution,Loopdistribution
inner,InnerLoopThreading,InnerLoopThreading,ASmallInnerLoopTest
testing,ASmallInnerLoopTest
interchange,Loopinterchange
isolation,Loopisolation
iteration,ParallelizingaSingleThreadedProgram
management,ParallelizingaSingleThreadedProgram
parallelizablealgorithms,ParallelizableAlgorithms,ParallelizingLoopsforMultiprocessorMachinesExampleClasses
printing,LoopPrinting,APrintingTest
testing,APrintingTest
processing,Retryingoperations
reimplementation,Loopreimplementation
scheduling,LoopSchedulingandLoadBalancing
synchronization,Reductionvariables
synchronizedblocks,LockStarvation
temporary,DeadlockDetection
testing,MultiprocessorScaling,ASimpleLoopTest
transformations,LoopAnalysisandTransformations
LWPs(lightweightprocesses),SolarisNativeThreads
M
mainmemory,TheVolatileKeyword,TheEffectofRegistersTheEffectofRegisters
registers,TheEffectofRegistersTheEffectofRegisters
volatilekeyword,TheVolatileKeyword
main()method,WhatIsaThread?
management,ThreadsandJavaSecurityThreadsandJavaSecurity,ParallelizingaSingleThreadedProgram
loops,ParallelizingaSingleThreadedProgram
security,ThreadsandJavaSecurityThreadsandJavaSecurity
maps,UsingtheCollectionClasses,WindowsNativeThreads
priorities(Win32),WindowsNativeThreads
markAsHardwait()method,DeadlockDetection
mayInterruptIfRunningflag,CallableTasksandFutureResults
measuringperformance,MeasuringJavaPerformance
memory,TheVolatileKeyword,TheEffectofRegistersTheEffectofRegisters,Threads,Stacks,andMemoryUsageStackAPIs
registers,TheEffectofRegistersTheEffectofRegisters
stacks,Threads,Stacks,andMemoryUsageStackAPIs
volatilekeyword,TheVolatileKeyword
methods,NonblockingI/O,WhatIsaThread?,TheThreadClass,TheThreadClass,StartingaThread,StartingaThread,TerminatingaThread,Pausing,
Suspending,andResumingThreads,Pausing,Suspending,andResumingThreads,Pausing,Suspending,andResumingThreads,Pausing,Suspending,
andResumingThreads,ThreadCleanup,SettingaFlag,DeterminingtheCurrentThread,MoreonRaceConditions,MoreonRaceConditions,Moreon
RaceConditions,MoreonRaceConditions,MoreonRaceConditions,MoreonRaceConditions,ExplicitLocking,ExplicitLocking,SynchronizedBlocks,
TheLockInterface,NestedLocks,WaitandNotify,WaitandNotify,TheWaitandNotifyMechanismandSynchronization,TheWaitandNotify
MechanismandSynchronization,wait(),notify(),andnotifyAll(),wait(),notify(),andnotifyAll(),ConditionVariables,OverviewoftheAtomicClasses,
OverviewoftheAtomicClasses,OverviewoftheAtomicClasses,OverviewoftheAtomicClasses,OverviewoftheAtomicClasses,Overviewofthe
AtomicClasses,Changingalgorithms,Changingalgorithms,Changingalgorithms,Retryingoperations,NotificationsandAtomicVariables,Compareand
set,Advancedatomicdatatypes,ThreadLocalVariables,InheritableThreadLocalVariables,SynchronizationTerms,SynchronizationTerms,Barrier,
Exchanger,Reader/WriterLocks,PreventingDeadlock,PreventingDeadlockwithTimeouts,DeadlockDetection,DeadlockDetection,DeadlockDetection,
DeadlockDetection,DeadlockDetection,DeadlockDetection,DeadlockDetection,DeadlockDetection,DeadlockDetection,ProcessingontheEvent
DispatchingThread,LongRunningEventCallbacks,LongRunningEventCallbacks,LongRunningEventCallbacks,LongRunningEventCallbacks,
ComplexSynchronization,ComplexSynchronization,IteratorsandEnumerations,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods,
SchedulingwithThreadPriorities,OtherThreadSchedulingMethods,OtherThreadSchedulingMethods,Executors,Executors,Executors,Executors,
Executors,Executors,Executors,Executors,RejectedTasks,CallableTasksandFutureResults,OverviewofTaskScheduling,OverviewofTask
Scheduling,OverviewofTaskScheduling,Thejava.util.TimerClass,Thejava.util.TimerClass,Thejava.util.TimerClass,Thejava.util.TimerClass,The
java.util.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,The
javax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,The
javax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,The
javax.swing.TimerClass,Thejavax.swing.TimerClass,Thejavax.swing.TimerClass,ATraditionalI/OServer,ATraditionalI/OServer,AnExample
MultithreadedServer,AnExampleMultithreadedServer,ScalingUsingTraditionalI/O,NonblockingI/O,ASingleThreadedNIOServer,ASingle
ThreadedNIOServer,InterruptedI/O,ThreadGroups,ThreadGroups,ThreadsandJavaSecurity,DaemonThreads,ThreadsandClassLoading,Threads
andClassLoading,ThreadsandExceptionHandling,StackAPIs,StackAPIs,StackAPIs,SynchronizedCollections,AtomicVariablesandContended
Synchronization,ParallelizingaSingleThreadedProgram,ParallelizingaSingleThreadedProgram,ParallelizingaSingleThreadedProgram,Parallelizinga
SingleThreadedProgram,LoopPrinting,LoopPrinting,LoopPrinting,LoopPrinting
accept(),ScalingUsingTraditionalI/O
actionPerformed(),TheThreadClass,LongRunningEventCallbacks
addActionListener(),Thejavax.swing.TimerClass
AtomicInteger.getAndIncrement(),AtomicVariablesandContendedSynchronization
await(),Barrier,DeadlockDetection
deadlock,DeadlockDetection
awaitTerminated(),Executors
blocks,SynchronizedBlocks
cancel(),Thejava.util.TimerClass
canThreadWaitOnLock(),DeadlockDetection
checkAccess(),ThreadsandJavaSecurity
childValue(),InheritableThreadLocalVariables
Collections.synchronizedCollection(),SynchronizedCollections
compareAndSet(),OverviewoftheAtomicClasses,Compareandset,Advancedatomicdatatypes
countStackFrames(),StackAPIs
criticalsections,SynchronizationTerms
crosscalling,NestedLocks
currentThread(),DeterminingtheCurrentThread
deadlock,PreventingDeadlock
exchange(),Exchanger
execute(),Executors
freeIfHardwait,DeadlockDetection
get(),OverviewoftheAtomicClasses
getAllLocksOwned(),DeadlockDetection
getAllStackTraces(),StackAPIs
getAllThreadsHardwaiting(),DeadlockDetection
getAndSet(),OverviewoftheAtomicClasses,Retryingoperations
getContextClassLoader(),ThreadsandClassLoading
getDelay(),Thejavax.swing.TimerClass
getInitialDelay(),Thejavax.swing.TimerClass
getListeners(),Thejavax.swing.TimerClass
getLogTimers(),Thejavax.swing.TimerClass
getStackTraces(),StackAPIs
getThreadgroup(),ThreadGroups
handleClient(),ASingleThreadedNIOServer
handleServer(),ASingleThreadedNIOServer
initialValue(),ThreadLocalVariables
interrupt(),InterruptedI/O,ThreadGroups
invokeAll(),Executors
invokeAny(),Executors
isAlive(),StartingaThread
isCoalesce(),Thejavax.swing.TimerClass
isEventDispatch(),LongRunningEventCallbacks
isRepeats(),Thejavax.swing.TimerClass
isRunning(),Thejavax.swing.TimerClass
isTerminated(),Executors
join(),ThreadCleanup,OverviewofTaskScheduling
listeners.toArray(),ComplexSynchronization
lock(),ExplicitLocking,DeadlockDetection
locks,SynchronizationTerms
loopDoRange(),ParallelizingaSingleThreadedProgram,ParallelizingaSingleThreadedProgram
loopGetRange(),ParallelizingaSingleThreadedProgram
loopProcess(),ParallelizingaSingleThreadedProgram
main(),WhatIsaThread?
markAsHardWait(),DeadlockDetection
newCharacter(),MoreonRaceConditions,MoreonRaceConditions,Changingalgorithms
synchronization,MoreonRaceConditions
newCondition(),ConditionVariables,Reader/WriterLocks
newUpdater(),OverviewoftheAtomicClasses
notify(),TheWaitandNotifyMechanismandSynchronization,wait(),notify(),andnotifyAll()
notifyAll(),wait(),notify(),andnotifyAll()
print(),LoopPrinting
println(),LoopPrinting,LoopPrinting
priorities,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods
purge(),CallableTasksandFutureResults
read(),NonblockingI/O
readByte(),AnExampleMultithreadedServer
readUTF(),NonblockingI/O
registerLock(),DeadlockDetection
removeActionListener(),Thejavax.swing.TimerClass
removeCharacterListener(),ComplexSynchronization
resetGenerator(),Changingalgorithms
resetTypist(),Changingalgorithms
restart(),Thejavax.swing.TimerClass
resume(),Pausing,Suspending,andResumingThreads,OtherThreadSchedulingMethods
run(),TheThreadClass
schedule(),Thejava.util.TimerClass
scheduleAtFixedRate(),Thejava.util.TimerClass
scheduledExecutionTime(),Thejava.util.TimerClass,Thejava.util.TimerClass
send2stream(),LoopPrinting
set(),OverviewoftheAtomicClasses
setCoalesce(),Thejavax.swing.TimerClass
setContextClassLoader(),ThreadsandClassLoading
setDaemon(),DaemonThreads
setDelay(),Thejavax.swing.TimerClass
setDone(),SettingaFlag,NotificationsandAtomicVariables
setInitialDelay(),Thejavax.swing.TimerClass
setLogTimers(),Thejavax.swing.TimerClass
setPriority(),SchedulingwithThreadPriorities
setRejectedExecutionHandler(),RejectedTasks
setRepeats(),Thejavax.swing.TimerClass
setScore(),MoreonRaceConditions
setText(),LongRunningEventCallbacks
setupDone(),LongRunningEventCallbacks
shutdown(),Executors
shutdownNow(),Executors
sleep(),Pausing,Suspending,andResumingThreads,Pausing,Suspending,andResumingThreads,OverviewofTaskScheduling
start(),StartingaThread,Thejavax.swing.TimerClass
startServer(),ATraditionalI/OServer,AnExampleMultithreadedServer
static,MoreonRaceConditions
stop(),TerminatingaThread,Thejavax.swing.TimerClass
stopServer(),ATraditionalI/OServer
submit(),Executors
suspend(),Pausing,Suspending,andResumingThreads,OtherThreadSchedulingMethods
Swingcomponentaccess,ProcessingontheEventDispatchingThread
synchronization,MoreonRaceConditions
toArray(),IteratorsandEnumerations
tryLock(),TheLockInterface,PreventingDeadlockwithTimeouts
uncaughtException(),ThreadsandExceptionHandling
unlock(),ExplicitLocking
unregisterLock(),DeadlockDetection
unsynchronizedbehavior,MoreonRaceConditions
voidnotify(),WaitandNotify
voidwait(),WaitandNotify
wait(),TheWaitandNotifyMechanismandSynchronization,OverviewofTaskScheduling
weakCompareAndSet(),OverviewoftheAtomicClasses
modification,Changingalgorithms,Bulkdatamodification,Bulkdatamodification
algorithms,Changingalgorithms
bulkdata,Bulkdatamodification,Bulkdatamodification
monitoringreachability,UsingtheTimer
monitors,SynchronizationTerms
Mozilla,JavaTerms
multiplecollections,UsingtheCollectionClasses
multipleobjects,locking,PreventingDeadlock
multiplethreads,MoreonRaceConditions,Deadlock,LockStarvation,PriorityBasedScheduling
competingforlocks,LockStarvation
deadlock,Deadlock
prioritybasedscheduling,PriorityBasedScheduling
synchronization,MoreonRaceConditions
multiplexing,I/O,NonblockingI/O
multiprocessorscaling,MultiprocessorScalingAPrintingTest
multiprocessorsystems,ParallelizingLoopsforMultiprocessorMachines
multithreadedservers,AnExampleMultithreadedServer
mutexes,TheSynchronizedKeyword,SynchronizationTerms
N
names,TheThreadClass
NativePosixThreadLibrary(NPTL),LinuxNativeThreads
nativethreads,WindowsNativeThreads,SolarisNativeThreads,LinuxNativeThreads
Linux,LinuxNativeThreads
Solaris,SolarisNativeThreads
Windows,WindowsNativeThreads
nestedlocks,NestedLocksNestedLocks
newCharacter()method,MoreonRaceConditions,MoreonRaceConditions,Changingalgorithms
synchronization,MoreonRaceConditions
newCondition()method,ConditionVariables,Reader/WriterLocks
newUpdater()method,OverviewoftheAtomicClasses
nonblockingI/O,NonblockingI/O,NonblockingI/O
notification,WaitandNotifyWaitandNotify,TheWaitandNotifyMechanismandSynchronization,TheWaitandNotifyMechanismand
Synchronization,NotificationsandAtomicVariables,ThreadNotificationCollectionClasses
(seealsowaitandnotifymechanism)
atomicvariables,NotificationsandAtomicVariables
collectionclasses,ThreadNotificationCollectionClasses
conditions,TheWaitandNotifyMechanismandSynchronization
waitingareas,WaitandNotifyWaitandNotify
notify()method,TheWaitandNotifyMechanismandSynchronization,wait(),notify(),andnotifyAll()
notifyAll()method,wait(),notify(),andnotifyAll()
NPTL(NativePosixThreadLibrary),LinuxNativeThreads
O
Objectclass,WaitandNotify
objects,ThreadsandObjects,ConditionVariables,ConditionVariables,Bulkdatamodification,SynchronizationTerms,Semaphore,PreventingDeadlock,
SwingThreadingRestrictions,ProcessingontheEventDispatchingThread,UsinginvokeLater()andinvokeAndWait(),UsinginvokeLater()and
invokeAndWait(),LongRunningEventCallbacksLongRunningEventCallbacks,UsingaThreadPool,ThreadsandClassLoadingThreadsandClass
Loading
bulkdatamodification,Bulkdatamodification
classloader,ThreadsandClassLoadingThreadsandClassLoading
Condition,ConditionVariables
Lock,ConditionVariables
locking,PreventingDeadlock
Runnable,UsingaThreadPool
semaphores,SynchronizationTerms,Semaphore
Swing,SwingThreadingRestrictions,ProcessingontheEventDispatchingThread,UsinginvokeLater()andinvokeAndWait(),UsinginvokeLater()
andinvokeAndWait(),LongRunningEventCallbacksLongRunningEventCallbacks
access,ProcessingontheEventDispatchingThread
invokeAndWait()method,UsinginvokeLater()andinvokeAndWait()
invokeLater()method,UsinginvokeLater()andinvokeAndWait()
longrunningeventcallbacks,LongRunningEventCallbacksLongRunningEventCallbacks
restrictions,SwingThreadingRestrictions
Opera,JavaTerms
operatingsystem(OS),TheSchedulingProcess,WindowsNativeThreads,SolarisNativeThreads,LinuxNativeThreads
Linuxnativethreads,LinuxNativeThreads
scheduling,TheSchedulingProcess
Solarisnativethreads,SolarisNativeThreads
Windowsnativethreads,WindowsNativeThreads
operations,retrying,Retryingoperations
optimisticsynchronization,SummaryofAtomicVariableUsage
ordering,statements,TheEffectofReorderingStatements
OS,LinuxNativeThreads(seeoperatingsystem)
outofmemoryerrors,OutofMemoryErrors
overflowerrors,StackOverflowErrors
P
parallelizablealgorithms,ParallelizableAlgorithms
parallelization,ASimpleLoopTest
parallelizingsinglethreadedprograms,ParallelizingaSingleThreadedProgramLoopPrinting
patterns,DoubleCheckedLocking,ThreadLocalVariables,TheProducer/ConsumerPatternTheProducer/ConsumerPattern,ScalingUsingTraditional
I/O
doublecheckedlocking,DoubleCheckedLocking
getter/setter,ThreadLocalVariables
producer/consumer,TheProducer/ConsumerPatternTheProducer/ConsumerPattern
TCPServerclass,ScalingUsingTraditionalI/O
pausingthreads,Pausing,Suspending,andResumingThreads
performance,WhyThreadPools?WhyNotThreadPools?,OverviewofPerformanceMeasuringJavaPerformance,SynchronizedCollections,Atomic
VariablesandContendedSynchronization,ThreadCreationandThreadPools
atomicvariables,AtomicVariablesandContendedSynchronization
pools,WhyThreadPools?WhyNotThreadPools?,ThreadCreationandThreadPools
synchronizedcollections,SynchronizedCollections
permissions,ThreadsandJavaSecurity
policies,security,ThreadsandJavaSecurityThreadsandJavaSecurity
polling,NonblockingI/O
pools,WhyThreadPools?WhyNotThreadPools?,ExecutorsExecutors,UsingaThreadPoolUsingaThreadPool,QueuesandSizesRejectedTasks,
QueuesandSizesRejectedTasks,ThreadCreation,CallableTasksandFutureResultsTheFutureTaskClass,SingleThreadedAccess,ThreadCreationand
ThreadPools
applying,UsingaThreadPoolUsingaThreadPool
callabletasks/futureresults,CallableTasksandFutureResultsTheFutureTaskClass
executors,ExecutorsExecutors
performance,ThreadCreationandThreadPools
queues,QueuesandSizesRejectedTasks
singlethreadedaccess,SingleThreadedAccess
sizes,QueuesandSizesRejectedTasks
threadcreation,ThreadCreation
preventingdeadlock,PreventingDeadlockPreventingDeadlockwithTimeouts
print()method,LoopPrinting
printing,LoopPrinting,APrintingTest
loops,LoopPrinting
testing,APrintingTest
println()method,LoopPrinting,LoopPrinting
priority,PriorityExceptions,Priorityinversion,Priorityinversion,Complexpriorities,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods
complex,Complexpriorities
exceptions,PriorityExceptions
inversion,Priorityinversion
scheduling,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods
prioritybasedscheduling,PriorityBasedScheduling
privateconnections,ATraditionalI/OServer
processingevents,Retryingoperations,ProcessingontheEventDispatchingThread
producer/consumerpattern,TheProducer/ConsumerPatternTheProducer/ConsumerPattern
programs,JavaTerms,WhatIsaThread?WhatIsaThread?,WhatIsaThread?,PreventingDeadlock
deadlock,PreventingDeadlock
starting,WhatIsaThread?
tasks,WhatIsaThread?WhatIsaThread?
purge()method,CallableTasksandFutureResults
Q
queries,flags,InterruptingaThread
queues,LockStarvation,TheProducer/ConsumerPattern,UsingtheCollectionClasses,QueuesandSizesRejectedTasks
collectionclasses,UsingtheCollectionClasses
lockacquisitions,LockStarvation
pools,QueuesandSizesRejectedTasks
producer/consumerpattern,TheProducer/ConsumerPattern
R
racecondition,TheExampleArchitecture,TheExampleArchitecture,DataSynchronization,TheSynchronizedKeyword,MoreonRaceConditionsMore
onRaceConditions,TheWaitandNotifyMechanismandSynchronization,Variablesubstitution,SwingThreadingRestrictions
(seealsosynchronization)
Swingobjects,SwingThreadingRestrictions
waitandnotifymechanism,TheWaitandNotifyMechanismandSynchronization
randomcharactergenerators,NotificationsandAtomicVariables,Usingthemultithreadedserver
RandomCharacterGeneratorclass,TheThreadClass,ScalingUsingTraditionalI/O
reachability,monitoring,UsingtheTimer
read()method,NonblockingI/O
readonlyvariables,Readonlyvariables
readByte()method,AnExampleMultithreadedServer
readerlocks,SynchronizationTerms,Reader/WriterLocks,LockStarvationandReader/WriterLocks
starvation,LockStarvationandReader/WriterLocks
readUTF()method,NonblockingI/O
reductionvariables,Reductionvariables,AReductionVariableTest
testing,AReductionVariableTest
ReentrantLockclass,NestedLocks,Reader/WriterLocks,DeadlockDetection,LockStarvation
lockstarvation,LockStarvation
ReentrantReadWriteLockclass,Reader/WriterLocks
registeredlockslist,DeadlockDetection
registerLock()method,DeadlockDetection
registers,TheEffectofRegistersTheEffectofRegisters
reimplementationofloops,Loopreimplementation
reinitializationofbarriers,Barrier
rejectedtasks,pools,RejectedTasks
RejectedExecutionException,RejectedTasks
RejectedExecutionHandlerinterface,RejectedTasks
releasinglocks,LockStarvation
removeActionListener()method,Thejavax.swing.TimerClass
removeCharacterListener()method,ComplexSynchronization
removingsynchronization,UsingtheAtomicClasses
reorderingstatements,TheEffectofReorderingStatements
resetGenerator()method,Changingalgorithms
resetTypist()method,Changingalgorithms
resolution,sleeptime,Pausing,Suspending,andResumingThreads
restart()method,Thejavax.swing.TimerClass
results,pools,CallableTasksandFutureResultsTheFutureTaskClass
resume()method,Pausing,Suspending,andResumingThreads,OtherThreadSchedulingMethods
resumingthreads,Pausing,Suspending,andResumingThreads
retryingoperations,Retryingoperations
run()method,TheThreadClass
Runnableinterface,CreatingaThread,TheRunnableInterfaceTheRunnableInterface
Runnableobject,UsingaThreadPool
runnablestate,scheduling,TheSchedulingProcess
runnabletargets,TheThreadClass
running,CompilingandRunningtheExamples,TheThreadClass
applications,TheThreadClass
code,CompilingandRunningtheExamples
RWLockclass,TheRWLockClass
S
scaling,ScalingUsingTraditionalI/O,MultiprocessorScalingAPrintingTest
I/Oservers,ScalingUsingTraditionalI/O
multiprocessor,MultiprocessorScalingAPrintingTest
schedule()method,Thejava.util.TimerClass
scheduleAtFixedRate()method,Thejava.util.TimerClass
scheduledExecutionTime()method,Thejava.util.TimerClass,Thejava.util.TimerClass
ScheduledThreadPoolExecutorclass,Thejavax.swing.TimerClass,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface
scheduling,LockStarvation,AnOverviewofThreadSchedulingComplexpriorities,PriorityBasedScheduling,SchedulingwithThreadPrioritiesOther
ThreadSchedulingMethods,PopularThreadingImplementationsLinuxNativeThreads,OverviewofTaskScheduling,Thejava.util.TimerClassUsing
theTimer,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface,Loop
SchedulingandLoadBalancing
implementations,PopularThreadingImplementationsLinuxNativeThreads
loops,LoopSchedulingandLoadBalancing
priorities,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods
prioritybased,PriorityBasedScheduling
tasks,OverviewofTaskScheduling,Thejava.util.TimerClassUsingtheTimer,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass,The
ScheduledThreadPoolExecutorClassUsingtheFutureInterface
java.util.Timerclass,Thejava.util.TimerClassUsingtheTimer
javax.swing.Timerclass,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass
ScheduledThreadPoolExecutorclass,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface
threads,LockStarvation
scopeoflocks,TheVolatileKeyword,LockScopeSynchronizedBlocks
ScoreLabelclass,ExplicitLocking,LockScopeSynchronizedBlocks
modifying,LockScopeSynchronizedBlocks
searchingdeadlocks,DeadlockDetection
security,ThreadsandJavaSecurityThreadsandJavaSecurity
selectinglocks,ChoosingaLockingMechanismTheLockInterface
selfscheduling,Selfscheduling
semaphores,SynchronizationTerms,Semaphore
send2stream()method,LoopPrinting
serialization,multiprocessorscaling,MultiprocessorScaling
servers,ATraditionalI/OServerScalingUsingTraditionalI/O,AnExampleMultithreadedServer,ANewI/OServerAMultithreadedNewI/OServer,A
SingleThreadedNIOServer,InterruptedI/OInterruptedI/O
I/O,ATraditionalI/OServerScalingUsingTraditionalI/O,ANewI/OServerAMultithreadedNewI/OServer,InterruptedI/OInterruptedI/O
interrupted,InterruptedI/OInterruptedI/O
JDK1.4,ANewI/OServerAMultithreadedNewI/OServer
multithreaded,AnExampleMultithreadedServer
singlethreadedNIO,ASingleThreadedNIOServer
ServerSocketclass,InterruptedI/O
servlets,JavaTerms
set()method,OverviewoftheAtomicClasses
setCoalesce()method,Thejavax.swing.TimerClass
setContextClassLoader()method,ThreadsandClassLoading
setDaemon()method,DaemonThreads
setDelay()method,Thejavax.swing.TimerClass
setDone()method,SettingaFlag,NotificationsandAtomicVariables
setInitialDelay()method,Thejavax.swing.TimerClass
setLogTimers()method,Thejavax.swing.TimerClass
setPriority()method,SchedulingwithThreadPriorities
setRejectedExecutionHandler()method,RejectedTasks
setRepeats()method,Thejavax.swing.TimerClass
sets,UsingtheCollectionClasses
setScore()method,MoreonRaceConditions
setText()method,LongRunningEventCallbacks
setting,SettingaFlag,TheLockInterface
flags,SettingaFlag
TimeUnitvalues,TheLockInterface
setuptime,multiprocessorscaling,MultiprocessorScaling
setupDone()method,LongRunningEventCallbacks
sharedvariables,Reductionvariables,Sharedvariables
sharing,WhatIsaThread?,WhatIsaThread?,MoreonRaceConditionsMoreonRaceConditions,ThreadLocalVariablesInheritableThreadLocal
Variables
access(heaps),WhatIsaThread?
data,WhatIsaThread?
raceconditions,MoreonRaceConditionsMoreonRaceConditions
threadlocalvariables,ThreadLocalVariablesInheritableThreadLocalVariables
shutdown()method,Executors
shutdownNow()method,Executors
signals,NonblockingI/O,SettingaFlag
simultaneousexecution,WhatIsaThread?
singlethreadedaccess,pools,SingleThreadedAccess
singlethreadedNIOservers,ASingleThreadedNIOServer
singlethreadedprograms,parallelizing,ParallelizingaSingleThreadedProgramLoopPrinting
sizes,QueuesandSizesRejectedTasks,SpecifyingStackSizes
pools,QueuesandSizesRejectedTasks
stacks,SpecifyingStackSizes
sleep()method,Pausing,Suspending,andResumingThreads,Pausing,Suspending,andResumingThreads,OverviewofTaskScheduling
softlocks,DeadlockDetection
Solarisnativethreads,SolarisNativeThreads
stacks,TheThreadClass,Threads,Stacks,andMemoryUsageStackAPIs
memory,Threads,Stacks,andMemoryUsageStackAPIs
size,TheThreadClass
start()method,StartingaThread,Thejavax.swing.TimerClass
startingthreads,CreatingaThread
startServer()method,ATraditionalI/OServer,AnExampleMultithreadedServer
starvation,locks,LockStarvationLockStarvationandReader/WriterLocks
statements,reordering,TheEffectofReorderingStatements
staticmethodsynchronization,MoreonRaceConditions
staticscheduling,Staticorchunkscheduling
stop()method,TerminatingaThread,Thejavax.swing.TimerClass
stoppingthreads,TwoApproachestoStoppingaThread
stopServer()method,ATraditionalI/OServer
storebackvariables,Storebackvariables
subclasses,ASingleThreadedNIOServer
submit()method,Executors
substitution,variables,Variablesubstitution
sumValuevariable,Reductionvariables
suspend()method,Pausing,Suspending,andResumingThreads,OtherThreadSchedulingMethods
suspendingthreads,Pausing,Suspending,andResumingThreads
Swing,SwingThreadingRestrictions,SwingThreadingRestrictionsSwingThreadingRestrictions,ProcessingontheEventDispatchingThread,Using
invokeLater()andinvokeAndWait(),UsinginvokeLater()andinvokeAndWait(),LongRunningEventCallbacksLongRunningEventCallbacks
access,ProcessingontheEventDispatchingThread
eventdispatchingthread,SwingThreadingRestrictionsSwingThreadingRestrictions
invokeAndWait()method,UsinginvokeLater()andinvokeAndWait()
invokeLater()method,UsinginvokeLater()andinvokeAndWait()
longrunningeventcallbacks,LongRunningEventCallbacksLongRunningEventCallbacks
restrictions,SwingThreadingRestrictions
synchronization,What'sNewinThisEdition?,MoreonRaceConditions,MoreonRaceConditions,MoreonRaceConditions,ExplicitLockingExplicit
Locking,TheWaitandNotifyMechanismandSynchronizationWaitandNotifyMechanismwithSynchronizedBlocks,WaitandNotifyMechanismwith
SynchronizedBlocks,ConditionVariablesConditionVariables,CanYouAvoidSynchronization?DoubleCheckedLocking,TheEffectofReordering
Statements,AtomicVariablesBulkdatamodification,SummaryofAtomicVariableUsage,ThreadLocalVariablesInheritableThreadLocalVariables,
SynchronizationTerms,SynchronizationClassesAddedinJ2SE5.0Reader/WriterLocks,PreventingDeadlockPreventingDeadlockwithTimeouts,
DeadlockDetectionDeadlockDetection,LockStarvationLockStarvationandReader/WriterLocks,LockStarvation,SynchronizationandCollection
ClassesThreadAwareClasses,ScalingUsingTraditionalI/O,SynchronizedCollections,AtomicVariablesandContendedSynchronization,Reduction
variables,Reductionvariables,MultiprocessorScaling
accept()method,ScalingUsingTraditionalI/O
atomicvariables,AtomicVariablesBulkdatamodification
blocks,WaitandNotifyMechanismwithSynchronizedBlocks,TheEffectofReorderingStatements,LockStarvation
waitandnotifymechanism,WaitandNotifyMechanismwithSynchronizedBlocks
classes,SynchronizationClassesAddedinJ2SE5.0Reader/WriterLocks
collectionclasses,SynchronizationandCollectionClassesThreadAwareClasses,SynchronizedCollections
conditionvariables,ConditionVariablesConditionVariables
contended,AtomicVariablesandContendedSynchronization
deadlock,PreventingDeadlockPreventingDeadlockwithTimeouts,DeadlockDetectionDeadlockDetection
detectionof,DeadlockDetectionDeadlockDetection
explicitlocking,ExplicitLockingExplicitLocking
lockstarvation,LockStarvationLockStarvationandReader/WriterLocks
loops,Reductionvariables
multiplethreads,MoreonRaceConditions
multiprocessorscaling,MultiprocessorScaling
newCharacter()method,MoreonRaceConditions
optimistic,SummaryofAtomicVariableUsage
sharedvariables,Reductionvariables
staticmethods,MoreonRaceConditions
terminology,SynchronizationTerms
threadlocalvariables,ThreadLocalVariablesInheritableThreadLocalVariables
waitandnotifymechanism,TheWaitandNotifyMechanismandSynchronizationWaitandNotifyMechanismwithSynchronizedBlocks
synchronizedkeyword,TheExampleArchitecture,TheSynchronizedKeywordTheSynchronizedKeyword,SynchronizedBlocks,Deadlockand
AutomaticLockReleases
blocks,SynchronizedBlocks
SynchronousQueue,QueuesandSizes
systemlevelthreads,GreenThreads
T
tasks,WhatIsaThread?WhatIsaThread?,CallableTasksandFutureResultsTheFutureTaskClass,OverviewofTaskScheduling,Thejava.util.Timer
ClassUsingtheTimer,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass,TheScheduledThreadPoolExecutorClassUsingtheFuture
Interface
pools,CallableTasksandFutureResultsTheFutureTaskClass
scheduling,OverviewofTaskScheduling,Thejava.util.TimerClassUsingtheTimer,Thejavax.swing.TimerClassUsingthejavax.swing.Timer
Class,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface
java.util.Timerclass,Thejava.util.TimerClassUsingtheTimer
javax.swing.Timerclass,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass
ScheduledThreadPoolExecutorclass,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface
TCPServerclass,ATraditionalI/OServer,ScalingUsingTraditionalI/O
patterns,ScalingUsingTraditionalI/O
temporaryloops,DeadlockDetection
termination,TerminatingaThread,TwoApproachestoStoppingaThread,InterruptingaThread
delays,InterruptingaThread
threads,TerminatingaThread,TwoApproachestoStoppingaThread
terminology,JavaTermsJavaVersions,Tools,andCode
testing,MeasuringJavaPerformance,MultiprocessorScaling,ASimpleLoopTest,AReductionVariableTest,ASmallInnerLoopTest,APrintingTest
innerloops,ASmallInnerLoopTest
loops,MultiprocessorScaling,ASimpleLoopTest,APrintingTest
printing,APrintingTest
performance,MeasuringJavaPerformance
reductionvariables,AReductionVariableTest
Threadclass,TheThreadClassTheThreadClass,TheLifecycleofaThreadThreadCleanup,TheRunnableInterfaceTheRunnableInterface
lifecycles,TheLifecycleofaThreadThreadCleanup
Runnableinterface,TheRunnableInterfaceTheRunnableInterface
threadawareclasses,ThreadAwareClasses
ThreadDeathclass,ThreadsandExceptionHandling,TheThreadDeathClass
ThreadLocalclass,ThreadLocalVariables
ThreadPoolclass,TheThreadPoolClass
ThreadPoolExecutorclass,UsingaThreadPool
threads,WhyThreads?ParallelizableAlgorithms,WhatIsaThread?,CreatingaThreadTheThreadClass,TheLifecycleofaThreadThreadCleanup,
CreatingaThread,CreatingaThread,TerminatingaThread,Pausing,Suspending,andResumingThreads,Pausing,Suspending,andResumingThreads,
Pausing,Suspending,andResumingThreads,ThreadCleanup,TwoApproachestoStoppingaThread,InterruptingaThread,ThreadsandObjects,Threads
andObjects,DeadlockDetection,LockStarvation,SwingThreadingRestrictionsSwingThreadingRestrictions,TheSchedulingProcess,GreenThreads,
ThreadGroups,DaemonThreads,ThreadCreationandThreadPools
cleanup,ThreadCleanup
creating,CreatingaThreadTheThreadClass,CreatingaThread
creation,ThreadCreationandThreadPools
eventdispatching,ThreadsandObjects,SwingThreadingRestrictionsSwingThreadingRestrictions
garbagecollection,WhatIsaThread?,TheSchedulingProcess,ThreadGroups,DaemonThreads
green,GreenThreads
interrupting,InterruptingaThread
lifecycles,TheLifecycleofaThreadThreadCleanup
objects,ThreadsandObjects
pausing,Pausing,Suspending,andResumingThreads
resuming,Pausing,Suspending,andResumingThreads
scheduling,LockStarvation
starting,CreatingaThread
stopping,TwoApproachestoStoppingaThread
suspending,Pausing,Suspending,andResumingThreads
terminating,TerminatingaThread
trees,DeadlockDetection
useof,WhyThreads?ParallelizableAlgorithms
threadsafe,ThreadsafeCollectionClasses,ThreadUnsafeCollectionClasses
classes,ThreadsafeCollectionClasses
unsafecollectionclasses,ThreadUnsafeCollectionClasses
throughput,pools,ThreadPoolsandThroughput
time,Thejava.util.TimerClassUsingtheTimer,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass
java.util.Timerclass,Thejava.util.TimerClassUsingtheTimer
javax.swing.Timerclass,Thejavax.swing.TimerClassUsingthejavax.swing.TimerClass
timeouts,PreventingDeadlockwithTimeouts,DeadlockDetection
deadlock,PreventingDeadlockwithTimeouts
softlocks,DeadlockDetection
Timerclass,UsingtheTimer,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface
ScheduledThreadPoolExecutorclass,TheScheduledThreadPoolExecutorClassUsingtheFutureInterface
websites,UsingtheTimer
TimerTaskclass,Thejava.util.TimerClass
TimeUnitvalues,TheLockInterface
toArray()method,IteratorsandEnumerations
tools,JavaVersions,Tools,andCode,CompilingandRunningtheExamples,TheBusyFlagClass,TheCondVarClass,TheBarrierClass,TheRWLock
Class,TheThreadPoolClass,TheJobSchedulerClass,TheDaemonLockClass
Ant,CompilingandRunningtheExamples
Barrierclass,TheBarrierClass
BusyFlagclass,TheBusyFlagClass
CondVarclass,TheCondVarClass
DaemonLockclass,TheDaemonLockClass
JobSchedulerclass,TheJobSchedulerClass
RWLockclass,TheRWLockClass
ThreadPoolclass,TheThreadPoolClass
trackingclients,ATraditionalI/OServer
transformations,loops,LoopAnalysisandTransformations
traversingthreadtrees,DeadlockDetection
trees,DeadlockDetection,DeadlockDetection
locks,DeadlockDetection
threads,DeadlockDetection
tryLock()method,TheLockInterface,PreventingDeadlockwithTimeouts
U
uncaughtException()method,ThreadsandExceptionHandling
uncontendedlocks,CanYouAvoidSynchronization?
unlock()method,ExplicitLocking
unregisterLock()method,DeadlockDetection
unsafecollectionclasses,ThreadUnsafeCollectionClasses
UnsupportedOperationException,Reader/WriterLocks
unsynchronizedmethodbehavior,MoreonRaceConditions
userdefinedscheduling,Userdefinedscheduling
userlevelthreads,GreenThreads
utilities,What'sNewinThisEdition?,JavaTerms,JavaVersions,Tools,andCode
(seealsotools)
utilityclasses,TheExampleArchitectureTheExampleArchitecture
V
values,TheLockInterface,TheEffectofRegisters,OverviewoftheAtomicClasses,Dataexchange,Advancedatomicdatatypes,Reductionvariables
atomicvariables,OverviewoftheAtomicClasses
dataexchange,Dataexchange
floatingpoint,Advancedatomicdatatypes
sumValuevariables,Reductionvariables
TimeUnit,TheLockInterface
variables,TheEffectofRegisters
variables,TheVolatileKeyword,ConditionVariablesConditionVariables,TheEffectofRegisters,AtomicVariablesBulkdatamodification,Overviewof
theAtomicClasses,Variablesubstitution,NotificationsandAtomicVariables,Dataexchange,Bulkdatamodification,ThreadLocalVariablesInheritable
ThreadLocalVariables,SynchronizationTerms,SynchronizationTerms,DeadlockDetection,SchedulingwithThreadPrioritiesOtherThreadScheduling
Methods,AtomicVariablesandContendedSynchronization,VariableClassifications,Loopprivatevariables,Readonlyvariables,Storebackvariables,
Reductionvariables,Reductionvariables,Reductionvariables,Sharedvariables,AReductionVariableTest
atomic,AtomicVariablesBulkdatamodification,NotificationsandAtomicVariables,Dataexchange,Bulkdatamodification,AtomicVariablesand
ContendedSynchronization
bulkdatamodification,Bulkdatamodification
dataexchange,Dataexchange
notification,NotificationsandAtomicVariables
performance,AtomicVariablesandContendedSynchronization
classifications,VariableClassifications
condition,ConditionVariablesConditionVariables,SynchronizationTerms,DeadlockDetection
deadlock,DeadlockDetection
event,SynchronizationTerms
loopprivate,Loopprivatevariables
priorities,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods
readonly,Readonlyvariables
reduction,Reductionvariables,AReductionVariableTest
testing,AReductionVariableTest
shared,Reductionvariables,Sharedvariables
storeback,Storebackvariables
substitution,Variablesubstitution
sumValue,Reductionvariables
threadlocal,ThreadLocalVariablesInheritableThreadLocalVariables
values,TheEffectofRegisters
volatile,OverviewoftheAtomicClasses
volatilekeyword,TheVolatileKeyword
Vectorclass,IteratorsandEnumerations
versions,JavaVersions,Tools,andCode
virtualmachine,JavaTerms,WhatIsaThread?,PreventingDeadlock,AnOverviewofThreadSchedulingComplexpriorities,SchedulingwithThread
PrioritiesOtherThreadSchedulingMethods,PopularThreadingImplementationsLinuxNativeThreads,ThreadGroupsThreadGroups,Threadsand
ExceptionHandlingTheThreadDeathClass,MultiprocessorScaling
deadlockdetection,PreventingDeadlock
exceptions,ThreadsandExceptionHandlingTheThreadDeathClass
executing,WhatIsaThread?
groups,ThreadGroupsThreadGroups
multiprocessorscaling,MultiprocessorScaling
scheduling,AnOverviewofThreadSchedulingComplexpriorities,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods,Popular
ThreadingImplementationsLinuxNativeThreads
implementations,PopularThreadingImplementationsLinuxNativeThreads
priorities,SchedulingwithThreadPrioritiesOtherThreadSchedulingMethods
voidnotify()method,WaitandNotify
voidwait()method,WaitandNotify
volatilekeyword,SettingaFlag,TheVolatileKeywordTheVolatileKeyword,Summary,CanYouAvoidSynchronization?,TheEffectofRegisters
volatilevariables,OverviewoftheAtomicClasses
W
wait()method,TheWaitandNotifyMechanismandSynchronization,OverviewofTaskScheduling
waitandnotifymechanism,WaitandNotify,TheWaitandNotifyMechanismandSynchronizationWaitandNotifyMechanismwithSynchronized
Blocks
synchronization,TheWaitandNotifyMechanismandSynchronizationWaitandNotifyMechanismwithSynchronizedBlocks
waitingareas,WaitandNotifyWaitandNotify
weakCompareAndSet()method,OverviewoftheAtomicClasses
webssites,Timerclass,UsingtheTimer
WELCOMEmessage,AnExampleMultithreadedServer
Windows,nativethreads,WindowsNativeThreads
writerlocks,SynchronizationTerms,Reader/WriterLocks,LockStarvationandReader/WriterLocks
starvation,LockStarvationandReader/WriterLocks
ScottOaksisaJavaTechnologistatSunMicrosystems,wherehehasworkedsince1987.WhileatSun,hehasspecializedinmanydisparatetechnologies,
fromtheSunOSkerneltonetworkprogrammingandRPCs.Since1995,he'sfocusedprimarilyonJavaandbringingJavatechnologytoendusers.Scott
alsoauthoredO'Reilly'sJavaSecurity,JavaThreadsandJiniinaNutshelltitles.
HenryWongisanindependentconsultant,involvedinvariousJavarelatedprojects.HenrypreviouslyworkedasacomputerengineeratSunMicrosystems
from1989to2003.Originallyhiredasaconsultanttohelpcustomerswithspecialdevicedrivers,kernelmodifications,andDOSinteroperabilityproducts,
HenryhasalsoworkedonSolarisports,performancetuningprojects,andmultithreadeddesignandimplementationsforbenchmarksanddemos.Sinceearly
1995,HenryhasbeeninvolvedindevelopingJavaprototypesandsupportingcustomerswhoareusingJava.
PriortoworkingatSun,HenryearnedaBachelorofSciencedegreeinchemicalengineeringfromTheCooperUnionin1987.Hejoinedasmallsoftware
companyin1986workingonSCSIdevicedrivers,imageandaudiodatacompression,andgraphicstoolsusedforamedicalinformationsystem.
Whennotinfrontofacomputer,Henryisaninstrumentratedprivatepilot,whoalsolikestodabbleinarchery,cooking,andtravelingtodifferentplaceswith
hiswife,Nini.
Colophon
Ourlookistheresultofreadercomments,ourownexperimentation,andfeedbackfromdistributionchannels.Distinctivecoverscomplementourdistinctive
approachtotechnicaltopics,breathingpersonalityandlifeintopotentiallydrysubjects.
TheanimalonthecoverofJavaThreads,ThirdEditionisamarineinvertebrate.Invertebrates,oranimalswithoutbackbones,makeupover97percentofall
animalspeciesontheplanet.Marineinvertebratesareabundantineveryocean,andincludesuchdiversespeciesascrabs,seacucumbers,jellyfish,starfish,
urchins,anemones,andshrimps.Oneofthemostintelligentanimalsinthesea,theoctopus,isalsoaninvertebrate.
Manyinvertebrateshaveprotectiveshellstoshieldthemfromhungry,razortoothedpredators.Youmaythinkthatinvertebrateswithoutshellswouldbe
particularlyvulnerable,butmanyhavedevelopedsomeeffectivedefenses.Seaanemonesbrandishtentaclesthatstingtheirenemies,urchinshavesharp
spikesthatcovertheirentirebodies,andseaslugsjustdon'ttasteverygood.
Thoughyoumaynotrealizeit,marineinvertebratesarequitebeneficialtohumans.Forone,theyconstituteahugefoodsource.Shrimps,crabs,octopuses,
clams,oysters,squids,lobsters,scallops,andcrayfisharealltastydelicacies.Invertebratesarealsonature'svacuumcleaners,takingindeadanddiscarded
materialandrecyclingitthroughthefoodchain.Andaftermillionsofyears,thebodiesofinvertebratessettleontheseafloorandformoildeposits,amajor
sourceoftheworld'senergy.
MattHutchinsonwastheproductioneditorforJavaThreads,ThirdEdition.OctalPublishing,Inc.providedproductionservices.SarahSherman,Marlowe
Shaeffer,andClaireCloutierprovidedqualitycontrol.
EmmaColbydesignedthecoverofthisbook,basedonaseriesdesignbyEdieFreedman.Thecoverimageisa19thcenturyengravingfromtheDover
PictorialArchive.EmmaColbyproducedthecoverlayoutwithQuarkXPress4.1usingAdobe'sITCGaramondfont.
DavidFutatodesignedtheinteriorlayout.ThisbookwasconvertedbyJoeWizdatoFrameMaker5.5.6withaformatconversiontoolcreatedbyErikRay,
JasonMcIntosh,NeilWalls,andMikeSierrathatusesPerlandXMLtechnologies.ThetextfontisLinotypeBirkatheheadingfontisAdobeMyriad
CondensedandthecodefontisLucasFont'sTheSansMonoCondensed.TheillustrationsthatappearinthebookwereproducedbyRobertRomanoand
JessamynReadusingMacromediaFreeHand9andAdobePhotoshop6.ThiscolophonwaswrittenbyMattHutchinson.
TheonlineeditionofthisbookwascreatedbytheSafariproductiongroup(JohnChodacki,BeckiMaisch,andEllieCutler)usingasetofFrametoXML
conversionandcleanuptoolswrittenandmaintainedbyErikRay,BennSalter,JohnChodacki,EllieCutler,andJeffLiggett.
,3
Java Threads
rd Edition
S COT T O A K S
HENRY WONG
Editor
DEB
CAMERON
Copyright2009O'ReillyMedia,Inc.
OReillyMedia
1005GravensteinHighwayNorth
Sebastopol,CA95472
20120819T16:03:1607:00