Sunteți pe pagina 1din 205

A Note Regarding Supplemental Files

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.

Who Should Read This Book?


ThisbookisintendedforprogrammersofalllevelswhoneedtolearntousethreadswithinJavaprograms.Thisincludesdeveloperswhohavepreviously
usedJavaandwrittenthreadedprogramsJ2SE5.0includesawealthofnewthreadrelatedclassesandfeatures.Therefore,evenifyou'vewrittenathreaded
programinJava,thisbookcanhelpyoutoexploitnewfeaturesofJavatowriteevenmoreeffectiveprograms.
ThefirstfewchaptersofthebookdealwiththeissuesofthreadedprogramminginJava,startingatabasiclevelnoassumptionismadethatthedeveloper
hashadanyexperienceinthreadedprogramming.Asthechaptersprogress,thematerialbecomesmoreadvanced,intermsofboththeinformationpresented
andtheexperienceofthedeveloperthatthematerialassumes.Fordeveloperswhoarenewtothreadedprogramming,thissequenceshouldprovideanatural
progressionofthetopic.
ThisbookisideallysuitedtodeveloperstargetingthesecondwaveofJavaprogramsmorecomplexprogramsthatfullyexploitthepowerofJava's
threadingsystem.WemaketheassumptionthatreadersofthebookarefamiliarwithJava'ssyntaxandfeatures.Inafewareas,wepresentcomplex
programsthatdependonknowledgeofotherJavafeatures:AWT,Swing,NIO,andsoon.However,thebasicprincipleswepresentshouldbe
understandablebyanyonewithabasicknowledgeofJava.We'vefoundthatbooksthatdealwiththeseotherAPIstendtogiveshortshrifttohowmultiple
threadscanfullyutilizethesefeaturesofJava(thoughdoubtlessthereverseistruewemakenoattempttoexplainnonthreadrelatedJavaAPIs).
Thoughthematerialpresentedinthisbookdoesnotassumeanypriorknowledgeofthreads,itdoesassumethatthereaderhasknowledgeofotherareasof
theJavaAPIandcanwritesimpleJavaprograms.

Versions Used in This Book


WritingabookonJavaintheageofInternettimeishardthesandonwhichwe'restandingisconstantlyshifting.Butwe'vedrawnalineinthatsand,and
thelinewe'vedrawnisattheJava2StandardEdition(J2SE)Version5.0fromSunMicrosystems.ThissoftwarewaspreviouslyknownasJ2SEVersion
1.5.
It'slikelythatversionsofJavathatpostdatethisversionwillcontainsomechangestothethreadingsystemnotdiscussedinthiseditionofthebook.Wewill
alsopointoutthedifferencesbetweenJ2SE5.0andpreviousversionsofJavaaswegosothatdevelopersusingearlierreleasesofJavawillalsobeableto
usethisbook.
MostofthenewthreadingfeaturesinJ2SE5.0areavailable(withdifferentAPIs)fromthirdpartiesforearlierversionsofJava(includingclasseswe
developedinearliereditionsofthisbook).Therefore,evenifyou'renotusingJ2SE5.0,you'llgetfullbenefitfromthetopicscoveredinthisbook.

What's New in This Edition?


ThiseditionincludesinformationaboutJ2SE5.0.OneofthemostsignificantchangesinJ2SE5.0istheinclusionofJavaSpecificationRequest(JSR)166,
oftenreferredtoasthe"concurrencyutilities."JSR166specifiesanumberofthreadrelatedenhancementstoexistingAPIsaswellasprovidingalarge
packageofnewAPIs.

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.

Organization of This Book


Here'sanoutlineofthebook,whichincludes15chaptersand1appendix:
Chapter1
Thischapterformsabasicintroductiontothetopicofthreads:whytheyareusefulandourapproachtodiscussingthem.

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.

Conventions Used in This Book


Thefollowingtypographicalconventionsareusedinthisbook:
Italic
IndicatesURLsandfilenames,andisusedtointroducenewterms.Sometimesweexplainthreadfeaturesusingaquestionandanswerformat.Questions
posedbythereaderarerenderedinitalic.
Constantwidth

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.

Java Versions, Tools, and Code


WealsoneedtobeconcernedwithspecificversionsofJavaitself.ThisisanartifactofthepopularityofJava,whichhasledtoseveralmajorenhancementsin
theplatform.Eachversionsupplementsthethreadrelatedclassesavailabletodevelopers,allowingthemtoworkwithnewfeaturesornolongertorelyon
externallydevelopedclasses.
[ 1 ]

WefocusinthisbookonJ2SE5.0.

Thisversioncontainsawealthofnewthreadrelatedclassesandfeatures.Theseclassesgreatlysimplifymuchofthe

workindevelopingthreadedapplicationssincetheyprovidebasicimplementationsofcommonthreadingparadigms.
ThenewfeaturesofJ2SE5.0areintegratedthroughouttheJavaplatformwe'veintegratedthenewfeaturesthroughoutourdiscussionaswell.Whenwe
discussJ2SE5.0,weclearlyidentifythenewfeaturesassuch.Ifyou'reunabletousethosefeaturesbecauseyoucannotyetupgradetheversionofJava
you'reusing,you'llfindsimilarfunctionalitytoalmostallJ2SE5.0featuresintheclassesprovidedintheAppendixA,whichcontainsimplementationsof
commonthreadingutilitiesthatweredevelopedinpreviousversionsofthisbooktheseutilitiesuseanearlierversionofJava.

ALLT HING SJ UST KEEPG ET T ING BET T ER


It'sinterestingtonotethedifferencesbetweenthiseditionofJavaThreadsandthepreviouseditions.Inearliereditionsofthisbook,wedeveloped
classestoperformexplicitlocks,conditionvariables,threadpooling,taskscheduling,andsoon.Allthatfunctionalityandmoreisnowincludedinthe
coreJ2SE5.0platform.InChapter14,welookatthreadperformancetheperformanceofbasicthreadrelatedoperations(andespecially
uncontendedlockacquisition)hasgreatlyimprovedsincewefirstlookedatthisinJDK1.1.Andinordertoobtainmeaningful,longrunningresultsfor
ourparallelismtestsinChapter15,wehadtoincreasethenumberofcalculationsbyasignificantfactor.

About the Examples


Fullcodetorunalltheexamplesinthisbookcanbedownloadedfromhttp://www.oreilly.com/catalog/jthreads3(http://www.oreilly.com/catalog/jthreads3).
Codeisorganizedbypackagesintermsofchapternumberandexamplenumber.Withinachapter,certainclassesapplytoallexamplesandareinthechapter
relatedpackage(e.g.,packagejavathreads.examples.ch02).Theremainingclassesareinanexamplespecificpackage(e.g.,package
javathreads.examples.ch02.example1).Packagenamesareshownwithinthetextforallclasses.
Exampleswithinachapter(andoftenbetweenchapters)tendtobeiterative,eachonebuildingontheclassesofpreviousexamples.Withinthetext,weuse
ellipsesincodesamplestoindicatethatthecodeisunchangedfrompreviousexamples.Forinstance,considerthispartialexamplefromChapter2:
packagejavathreads.examples.ch02.example2;
...
publicclassSwingTypeTesterextendsJFrame{
...
privateJButtonstopButton;
...
privatevoidinitComponents(){
...
stopButton=newJButton();

ThepackagenametellsusthatthisisthesecondexampleinChapter2.Followingtheellipses,weseethatthereisanewinstancevariable(stopButton)
andsomenewcodeaddedtotheinitComponents()method.
Forreferencepurposes,welisttheexamplesandtheirmainclassattheendofeachchapter.

Compiling and Running the Examples


ThecodeexamplesarewrittentobecompiledandrunonJ2SE5.0.WeuseseveralnewclassesofJ2SE5.0throughouttheexamplesandoccasionallyuse
newlanguagefeaturesofJ2SE5.0aswell.Thismeansthatclassesmustbecompiledwithasourceargument:
piccolo%javasource1.5javathreads/examples/ch02/example1/*.java

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.

Alarms and Timers


Traditionaloperatingsystemstypicallyprovidesomesortoftimeroralarmcall:theprogramsetsthetimerandcontinuesprocessing.Whenthetimer
expires,theprogramreceivessomesortofasynchronoussignalthatnotifiestheprogramofthetimer'sexpiration.
InearlyversionsofJava,theprogrammerhadtosetupaseparatethreadtosimulateatimer.Thatthreadsleptforthedurationofaspecifiedtimeintervaland
thennotifiedotherthreadswhenthetimerexpired.AsJavamatured,multiplenewclassesthatprovidethisfunctionalitywereadded.Thesenewclassesuse

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.

Chapter2.Thread Creation and Management


Inthischapter,wecoverallthebasicsaboutthreads:whatathreadis,howthreadsarecreated,andsomedetailsaboutthelifecycleofathread.Ifyou'renew
tothreading,thischaptergivesyoualltheinformationyouneedtocreatesomebasicthreads.Beaware,however,thatwetakesomeshortcutswithour
examplesinthischapter:it'simpossibletowriteagoodthreadedprogramwithouttakingintoaccountthedatasynchronizationissuesthatwediscussin
Chapter3.Thischaptergetsyoustartedonunderstandinghowthreadsworkcoupledwiththenextchapter,you'llhavetheabilitytostartusingthreadsin
yourownJavaapplications.

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.

START ING APRO G RAM


ForJavaapplications,executionbeginswiththemain()methodoftheclassbeingrun.WhataboutotherJavaprograms?
Inapplets,servlets,andotherJ2EEprograms,executionstillbeginswiththemain()methodoftheprogram,butinthiscase,themain()method
belongstotheJavapluginorJ2EEcontainer.Thosecontainersthencallyourcodethroughpredetermined,wellknownlocations.Anappletiscalled
viaitsinit()andstart()methodsaservletiscalledthroughitsdoGet()anddoPost()methods,andsoon.
Inanycase,theprocedureisthesame:executionofyourcodebeginswiththefirststatementsandproceedsbyasinglethreadsequentially.

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.

The Example Architecture


Beforewedelveintothethreadingaspectsofourcode,let'slookatafewutilityclassesusedinthisandsubsequentexamples.Thetypinggamehastwo
sourcesforcharacters:charactersthattheusertypesatthekeyboardandcharactersthatarerandomlygenerated.Bothsourcesofcharactersarerepresentedby
thisinterface:
packagejavathreads.examples.ch02;

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).

REAL LIF ERACECO NDIT IO NS


Inordertounderstandthreadedprogrammingfully,youmustunderstandhowthreadsrunandarecreated(thetopicofthischapter)aswellashow
theyinteractwithdata(thetopicofthenextchapter).Anyinterestingthreadedprogramusesbothfeatures.
Thismeansthataforwardreferencetosomedetails(likethesynchronizedkeyword)isunavoidable.Thisistheessenceofaracecondition:two
thingsneedtocompletesequentiallyinordertoendupinacoherentstate.
ThisraceconditionalsoappliestoSwingprogramming.WeuseSwingcomponentsinourexamplesbecausetheymaketheapplicationsmorerelevant
andinteresting.Swingcomponentshavesomespecialthreadprogrammingconsiderations,aswe'llseeoverthenextfewchapters,butwewon'tbe
abletoexplainthemfullyuntilweunderstandmoreabouthowmultiplethreadswork.

The Thread Class

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.

The Lifecycle of a Thread


Inourexample,weglossoversomeofthedetailsofhowthethreadisactuallystarted.We'lldiscussthatinmoredepthnowandalsogivedetailsonother
lifecycleeventsofathread.ThelifecycleitselfisshowninFigure24.ThemethodsoftheThreadclassthataffectthethread'slifecycleare:
packagejava.lang;
publicclassThreadimplementsRunnable{
publicvoidstart();
publicvoidrun();
publicvoidstop();//Deprecated,donotuse
publicvoidresume();//Deprecated,donotuse
publicvoidsuspend();//Deprecated,donotuse
publicstaticvoidsleep(longmillis);
publicstaticvoidsleep(longmillis,intnanos);
publicbooleanisAlive();
publicvoidinterrupt();
publicbooleanisInterrupted();
publicstaticbooleaninterrupted();
publicvoidjoin()throwsInterruptedException;
}

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.

Pausing, Suspending, and Resuming Threads


Onceathreadbeginsexecutingitsrun()method,itcontinuesexecutionuntiltherun()methodcompletes.Ifyou'refamiliarwithotherthreadmodels,you
mayknowofaconceptcalledthreadsuspension,whereathreadistoldtopauseitsexecution.Later,thethreadisresumed,whichistosaythatitistoldto
continueitsexecution.TheThreadclasscontainssuspend()andresume()methods,buttheysufferfromthesameraceconditionproblemasthe
stop()method,andthey,too,aredeprecated.
Itispossibleforathreadtosuspenditsownexecutionforaspecificperiodoftimebycallingthesleep()method.Weusethatmethodinour

RandomCharacterGeneratorthread.Whenathreadexecutesthesleep()method,itpausesforagivennumberofmilliseconds(ormillisecondsplus
nanoseconds),duringwhichitissaidtobeasleep.Whenthepausetimehaselapsed,thethreadwakesupandcontinuesexecutionwiththestatements
immediatelyfollowingthesleep()method.

SLEEPT IMERESO LUT IO N


TheThreadclassprovidesaversionofthesleep()methodthatallowsthedevelopertospecifythetimeinnanoseconds.MostJavavirtualmachines
donotsupportthissortofprecisetiming.Whenthesleep()methodexecutes,itroundsthenanosecondargumenttothenearestmillisecond.Infact,
mostoperatingsystemsthenfurtheradjustthemillisecondargumentsothatitisamultipleofsomenumber:e.g.,20or50milliseconds.Consequently,
theleastamountoftimethatyoucansleeponmostJavaimplementationsis20or50milliseconds.
NotethatthisistrueeveninJ2SE5.0,whichintroducesothernanosecondtimefunctionality(e.g.,theSystem.nanoTime()method).Theresolutionof
thesleep()methodisstillonlygoodtoafewmilliseconds.
OngoingprojectswithintheJavaCommunityProcessareworkingonarealtimeJavaimplementationonsuchanimplementation,theprecise
resolutionspecifiedinthesleep()methodmayeventuallyberealized.Formostplatforms,developerscannotdesignapplicationsthatrequiresupport
ofnanoseconds(orevenexactmilliseconds).

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).

Two Approaches to Stopping a Thread


Whenyouwantathreadtoterminatebasedonsomecondition(e.g.,theuserhasquitthegame),youhaveseveralapproachesavailable.Hereweofferthe
twomostcommon.

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.

The Runnable Interface


Whenwetalkedaboutcreatingathread,wementionedtheRunnableinterface(java.lang.Runnable).TheThreadclassimplementsthisinterface,
whichcontainsasinglemethod:

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.

Threads and Objects


Let'stalkalittlemoreabouthowthreadsinteract.ConsidertheRandomCharacterGeneratorthread.Wesawhowanotherclass(the
SwingTypeTesterclass)keptareferencetothatthreadandhowitcontinuedtocallmethodsonthatobject.
AlthoughthosemethodsaredefinedintheRandomCharacterGeneratorclass,theyarenotexecutedbythatthread.Instead,methodslikethesetDone(
)methodareexecutedbytheSwingeventdispatchingthreadasitexecutestheactionPerformed()methodwithintheSwingTypeTesterclass.Asfar
asthevirtualmachineisconcerned,thesetDone()methodisjustaseriesofstatementsthosestatementsdonot"belong"toanyparticularthread.
Therefore,theeventdispatchingthreadexecutesthesetDone()methodinexactlythesamewayinwhichitexecutesanyothermethod.
Thispointisoftenconfusingtodeveloperswhoarenewtothreadsitcanbeconfusingaswelltodeveloperswhounderstandthreadsbutarenewtoobject
orientedprogramming.InJava,aninstanceoftheThreadclassisjustanobject:itmaybepassedtoothermethods,andanythreadthathasareferenceto
anotherthreadcanexecuteanymethodofthatotherthread'sThreadobject.TheThreadobjectisnotthethreaditselfitisinsteadasetofmethodsanddata
thatencapsulatesinformationaboutthethread.Andthatmethodanddatacanbeaccessedbyanyotherthread.
Foramorecomplexexample,examinetheAnimatedCharacterCanvasclassanddeterminehowmanythreadsexecutesomeofitsmethods.You
shouldbecomfortablewiththefactthatfourdifferentthreadsusethisobject.TheRandomCharacterGeneratorthreadinvokesthenewChar()method
onthatobject.Thetimingthreadinvokestherun()method.ThesetDone()methodisinvokedbytheSwingeventdispatchingthread.Andthe
constructoroftheclass(i.e.,thedefaultconstructor)isinvokedbythemainmethodoftheapplicationasitconstructstheGUI.
Theupshotofthisisthatyoucannotlookatanyobjectsourcecodeandknowwhichthreadisexecutingitsmethodsorexaminingitsdata.Youmaybe
temptedtolookataclassoranobjectandwonderwhichthreadisrunningthecode.TheanswerevenifthecodeiswithaclassthatextendstheThread
classisthatanyofpotentiallythousandsofthreadscouldbeexecutingthecode.

Determining the Current Thread


Sometimes,youneedtofindoutwhatthecurrentthreadis.Inthemostcommoncase,codethatbelongstoanarbitraryobjectmayneedtoinvokeamethod
ofthethreadclass.Inothercircumstances,codewithinathreadobjectmaywanttoseeifthecodeisbeingexecutedbythethreadrepresentedbytheobjector
byacompletelydifferentthread.
YoucanretrieveareferencetothecurrentthreadbycallingthecurrentThread()method(astaticmethodoftheThreadclass).Therefore,toseeifcode
isbeingexecutedbyanarbitrarythread(asopposedtothethreadrepresentedbytheobject),youcanusethispattern:

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.

The Synchronized Keyword


Let'srevisitourAnimatedDisplayCanvasclassfromthepreviouschapter:

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.

DEF INIT IO N:ATO MIC


Thetermatomicisrelatedtotheatom,onceconsideredthesmallestpossibleunitofmatter,unabletobebrokenintoseparateparts.Whencomputer
codeisconsideredatomic,itcannotbeinterruptedduringitsexecution.Thiscaneitherbeaccomplishedinhardwareorsimulatedinsoftware.
Generally,atomicinstructionsareprovidedinhardwareandareusedtoimplementatomicmethodsinsoftware.
Inourcase,wedefineatomiccodeascodethatcan'tbefoundinanintermediatestate.Inouranimatedcanvasexample,iftheactsof"resettingthe
variable"and"redrawingoneframeoftheanimation"wereatomic,itwouldnotbepossibletosetthevariableattheverymomentthatthecharacteris
beinganimated.Theanimationthreadalsocouldn'tfindthevariablesinanintermediatestate.

TheJavaspecificationprovidescertainmechanismsthatdealspecificallywiththisproblem.TheJavalanguageprovidesthesynchronizedkeywordin
comparisonwithotherthreadingsystems,thiskeywordallowstheprogrammeraccesstoaresourcethatisverysimilartoamutexlock.Forourpurposes,it
simplypreventstwoormorethreadsfromcallingthemethodsofthesameobjectatthesametime.

DEF INIT IO N:MUT EXLO CK


Amutexlockisalsoknownasamutuallyexclusivelock.Thistypeoflockisprovidedbymanythreadingsystemsasameansofsynchronization.
Onlyonethreadcangrabamutexatatime:iftwothreadstrytograbamutex,onlyonesucceeds.Theotherthreadhastowaituntilthefirstthread
releasesthelockbeforeitcangrabthelockandcontinueoperation.
InJava,everyobjecthasanassociatedlock.Whenamethodisdeclaredsynchronized,theexecutingthreadmustgrabthelockassociatedwiththe
objectbeforeitcancontinue.Uponcompletionofthemethod,thelockisautomaticallyreleased.

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.

The Volatile Keyword


Thereisstillonemorethreadingissueinthisexample,andithastodowiththesetDone()method.Thismethodiscalledfromtheeventdispatching
threadwhentheStopbuttonispresseditiscalledbyaneventhandler(anactionPerformed()method)thatisdefinedasaninnerclasstothe
SwingTypeTesterclass.Theissuehereisthatthismethodisexecutedbytheeventdispatchingthreadandchangesdatathatisbeingusedbyanother
thread:thedoneflag,whichisaccessedbythethreadoftheAnimatedDisplayCanvasclass.
So,can'twejustsynchronizethetwomethods,justaswedidpreviously?Yesandno.Yes,Java'ssynchronizedkeywordallowsthisproblemtobefixed.
Butno,thetechniquesthatwehavelearnedsofarwillnotwork.Thereasonhastodowiththerun()method.Ifwesynchronizedboththerun()and
setDone()methods,howwouldthesetDone()methodeverexecute?Therun()methoddoesnotexituntilthedoneflagisset,butthedoneflag
can'tbesetbecausethesetDone()methodcan'texecuteuntiltherun()methodcompletes.

DEF INIT IO N:SCO PEO F ALO CK


Thescopeofalockisdefinedastheperiodoftimebetweenwhenthelockisgrabbedandreleased.Inourexamplessofar,wehaveusedonly
synchronizedmethodsthismeansthatthescopeoftheselocksistheperiodoftimeittakestoexecutethemethods.Thisisreferredtoasmethod
scope.
Laterinthischapter,we'llexaminelocksthatapplytoanyblockofcodeinsideamethodorthatcanbeexplicitlygrabbedandreleasedtheselocks
haveadifferentscope.We'llexaminethisconceptofscopeaslocksofvarioustypesareintroduced.

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.

More on Race Conditions


Let'sexamineamorecomplexexamplesofar,wehavelookedatsimpledatainteractionusedeitherforloopcontrolorforredrawing.Inthisnextiterationof
ourtypinggame,weshareusefuldatabetweenthethreadsinordertocalculateadditionaldataneededbytheapplication.
Ourapplicationhasadisplaycomponentthatpresentsrandomnumbersandlettersandacomponentthatshowswhattheusertyped.Whiletherearedata
synchronizationissuesbetweenthethreadsofthisexample,thereislittleinteractionbetweenthesetwoactions:theactoftypingaletterdoesnotdependon
theanimationletterthatisshown.Butnowwewilldevelopascoringsystem.Usersseefeedbackonwhethertheycorrectlytypedwhatwaspresented.Our
newcodemustmakethiscomparison,anditmustmakesurethatnoraceconditionexistswhencomparingthedata.
Toaccomplishthis,wewillintroduceanewcomponent,onethatdisplaystheuser'sscore,whichisbasedonthenumberofcorrectandincorrectresponses:

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.

WHENISARACECO NDIT IO NAPRO BLEM?


Araceconditionoccurswhentheorderofexecutionoftwoormorethreadsmayaffectsomevariableoroutcomeintheprogram.Itmayturnoutthat
allthedifferentpossibleordersofthreadexecutionhavethesamefinaleffectontheprogram:theeffectcausedbytheraceconditionmaybe
insignificantandmaynotevenberelevant.Forexample,iftheanimationthreaddrawsthepreviouscharacterinsteadofthenewcharacter,itisnota
problemifthecharacterhasalreadybeentypedsincethenewcharacterisdrawninthenextrepaintiteration.Alternatively,thetimingofthethreading
systemmaybesuchthattheraceconditionnevermanifestsitself,despitethefactthatitexistsinthecode.
Raceconditionscanbeconsideredharmless(orbenign)ifyoucanprovethattheresultoftheraceconditionisalwaysthesame.Thisisacommon
techniqueinsomeofJava'scoreclasses(mostcommonly,theatomicclassesdiscussedinChapter5)we'llseeafewexamplesofitinthisbook.But
ingeneral,araceconditionisaproblemthatiswaitingtohappen.Simplechangesinthealgorithmcancauseraceconditionstomanifestthemselvesin
problematicways.Sincedifferentvirtualmachineshavedifferentorderingofthreadexecution,thedevelopershouldneverletaraceconditionexist
evenifitiscurrentlynotcausingaproblemonthedevelopmentsystem.

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.

SYNCHRO NIZ EDMET HO DSVERSUSSYNCHRO NIZ EDBLO CKS


Itispossibletouseonlythesynchronizedblockmechanismevenwhenweneedtosynchronizethewholemethod.Forclarityinthisbook,we
synchronizethewholemethodwiththesynchronizedmethodmechanismandusethesynchronizedblockmechanismotherwise.Itisthe
programmer'spersonalpreferencetodecidewhentosynchronizeonablockofcodeandwhentosynchronizethewholemethodwiththecaveat
thatit'salwaysbettertoestablishassmallalockscopeaspossible.

Choosing a Locking Mechanism


IfwecompareourfirstimplementationoftheScoreLabelclass(usingsynchronizedmethods)tooursecond(usinganexplicitlock),it'seasytoconclude
thatusingtheexplicitlockisnotaseasyasusingthesynchronizedkeyword.Withthekeyword,wedidn'tneedtocreatethelockobject,wedidn'tneedto
callthelockobjecttograbandreleasethelock,andwedidn'tneedtoworryaboutexceptions(therefore,wedidn'tneedthetry/finallyclause).So,which
techniqueshouldyouuse?Thatisuptoyouasadeveloper.Itispossibletouseexplicitlockingforeverything.Itispossibletocodetojustusethe
synchronizedkeyword.Anditispossibletouseacombinationofboth.Formorecomplexthreadprogramming,however,relyingsolelyonthe
synchronizedkeywordbecomesverydifficult,aswewillsee.
Howarethelockclassesrelatedtostaticmethods?Forstaticmethods,theexplicitlocksareactuallysimplertounderstandthanthesynchronized
keyword.Lockobjectsareindependentoftheobjects(andconsequently,methods)thatusethem.Asfaraslockobjectsareconcerned,itdoesn'tmatterifthe
methodbeingexecutedisstaticornot.Aslongasthemethodhasareferencetothelockobject,itcanacquirethelock.Forcomplexsynchronizationthat
involvesbothstaticandnonstaticmethods,itmaybeeasiertousealockobjectinsteadofthesynchronizedkeyword.
Synchronizingentiremethodsisthesimplesttechnique,butaswehavealreadymentioned,itispossiblethatdoingsocreatesalockwhosescopeistoolarge.
Thiscancausemanyproblems,includingcreatingadeadlocksituation(whichweexaminelaterinthischapter).Itmayalsobeinefficienttoholdalockfor
thesectionofcodewhereitisnotactuallyneeded.
Usingthesynchronizedblockmechanismmayalsobeaproblemiftoomanyobjectsareinvolved.Asweshallsee,itisalsopossibletohaveadeadlock
conditionifwerequiretoomanylockstobeacquired.Thereisalsoaslightoverheadingrabbingandreleasingthelock,soitmaybeinefficienttofreealock
justtograbitagainafewlinesofcodelater.Synchronizedblocksalsocannotestablishalockscopethatspansmultiplemethods.
Intheend,whichtechniquetouseisoftenamatterofpersonalpreference.Inthisbook,weusebothtechniques.Wetendtofavorusingexplicitlocksinthe
latersectionsofthisbook,mainlybecauseweusefunctionalitythattheLockinterfaceprovides.

The Lock Interface


Let'slookalittledeeperintotheLockinterface:
publicinterfaceLock{
voidlock();
voidlockInterruptibly()throwsInterruptedException;
booleantryLock();
booleantryLock(longtime,TimeUnitunit)
throwsInterruptedException;
voidunlock();
ConditionnewCondition();
}

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.

Wait and Notify


We'veseenthateveryJavaobjecthasalock.Inaddition,everyobjectalsoprovidesamechanismthatallowsittobeawaitingareathismechanismaids
[ 1 ]

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.

WAIT ( ) ,NO T IF Y( ) ,ANDT HEO BJ ECT CLASS


Justlikethesynchronizedmethod,thewaitandnotifymechanismisavailablefromeveryobjectintheJavasystem.However,thismechanismis
accomplishedbymethodinvocationswhereasthesynchronizedmechanismishandledbyaddingakeyword.
Thewait()andnotify()mechanismworksbecausethesearemethodsoftheObjectclass.SinceallobjectsintheJavasysteminheritdirectlyor
indirectlyfromtheObjectclass,allobjectsarealsoinstancesoftheObjectclassandthereforehavesupportforthismechanism.

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.

The Wait-and-Notify Mechanism and Synchronization


Aswejustmentioned,thewaitandnotifymechanismhasaraceconditionthatneedstobesolvedwiththesynchronizationlock.Itisnotpossibletosolvethe
raceconditionwithoutintegratingthelockintothewaitandnotifymechanism.Thisiswhyitismandatoryforthewait()andnotify()methodstohold
thelocksfortheobjectonwhichtheyareoperating.
Thewait()methodreleasesthelockpriortowaitingandreacquiresthelockpriortoreturningfromthewait()method.Thisisdonesothatnorace
conditionexists.Asyourecall,thereisnoconceptofreleasingandreacquiringalockintheJavaAPI.Thewait()methodisactuallytightlyintegratedwith
thesynchronizationlock,usingafeaturenotavailabledirectlyfromthesynchronizationmechanism.Inotherwords,itisnotpossibleforustoimplementthe
wait()methodpurelyinJava:itisanativemethod.
Thisintegrationofthewaitandnotifymechanismandthesynchronizationlockistypical.Inothersystems,suchasSolarisorPOSIXthreads,condition
variablesalsorequirethatamutexlockbeheldforthemechanismtowork.
Inourexample,boththerun()andthesetDone()methodsaresynchronized.Inthepreviouschapter,thiswasnotarecommendedtechniquesincethe
run()methodnevercompletesinfact,someofourexamplesshowedhowtheapplicationbrokeasaresultofsynchronizingtherun()method.
However,becauseofthewaythewait()methodworks,thereisnolongeradangerofdeadlockintheexamplewe'vejustshown.Thewait()method
releasesthelock,whichallowsotherthreadstoexecute,includingthethreadthateventuallyexecutesthesetDone()method.Beforethewait()method
returns,itreacquiresthelock.Tothedeveloper,itappearsasifthelockhasbeenheldtheentiretime.
Whathappenswhennotify()iscalledandnothreadiswaiting?Thiscannothappeninouranimationcomponent.Sincetherun()methoddoesnotexit,
itisnotpossibleforthelocktobefreedwithoutthethreadbeinginawait()methodcall.However,ingeneralthisisnotthecase:itisnotrequiredthat

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.

wait( ), notify( ), and notifyAll( )


Whathappenswhenmorethanonethreadiswaitingfornotification?Whichthreadsactuallygetthenotificationwhenthenotify()methodiscalled?It
depends:theJavaspecificationdoesn'tdefinewhichthreadgetsnotified.Whichthreadactuallyreceivesthenotificationvariesbasedonseveralfactors,
includingtheimplementationoftheJavavirtualmachineandschedulingandtimingissuesduringtheexecutionoftheprogram.Thereisnowayto
determine,evenonasingleprocessorplatform,whichofmultiplethreadsreceivesthenotification.
AnothermethodoftheObjectclassassistsuswhenmultiplethreadsarewaitingforacondition:

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.

Wait-and-Notify Mechanism with Synchronized Blocks


Inourexample,weshowedhowthewait()andnotify()methodsarecalledwithinasynchronizedmethod.Inthatcase,thelockthatinteractswiththe
wait()andnotify()methodsistheobjectlockofthethisobject.
Itispossibletousethewait()andnotify()methodswithasynchronizedblock.Inthatcase,thelockthatthecodeholdsisprobablynottheobjectlock
ofthecode:itismostlikelythelockofsomeobjectexplicitlyspecifiedinthesynchronizedblock.Therefore,youmustinvokethewait()ornotify()
methodonthatsameobject,likethis:
packagejavathreads.examples.ch04.example2;
...
publicclassAnimatedCharacterDisplayCanvasextendsCharacterDisplayCanvas
implementsCharacterListener,Runnable{
...
privateObjectdoneLock=newObject();

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.

Chapter5.Minimal Synchronization Techniques


Intheprevioustwochapters,wediscussedwaysofmakingobjectsthreadsafe,allowingthemtobeusedbytwoormorethreadsatthesametime.Thread
safetyisthemostimportantaspectofgoodthreadprogrammingraceconditionsareextremelydifficulttoreproduceandfix.
Inthischapter,wecompleteourdiscussionofdatasynchronizationandthreadsafetybyexaminingtworelatedtopics.WebeginwithadiscussionoftheJava
memorymodel,whichdefineshowvariablesareactuallyaccessedbythreads.Thismodelhassomesurprisingramificationsoneoftheissuesthatwe'llclear
upfromourpreviouschaptersisjustwhatitmeansforathreadtobemodeledasalistofinstructions.Afterexplainingthememorymodel,wediscusshow
volatilevariablesfitintoitandwhytheycanbeusedsafelyamongmultiplethreads.Thistopicisallaboutavoidingsynchronization.
Wethenexamineanotherapproachtodatasynchronization:theuseofatomicclasses.Thissetofclasses,introducedinJ2SE5.0,allowscertainoperationson
certaintypesofdatatobedefinedatomically.Theseclassesprovideanicedataabstractionfortheoperationswhilepreventingtheraceconditionsthatwould
otherwisebeassociatedwiththeoperation.Theseclassesarealsointerestingbecausetheytakeadifferentapproachtosynchronization:ratherthanexplicitly
synchronizingaccesstothedata,theyuseanapproachthatallowsraceconditionstooccurbutensuresthattheraceconditionsareallbenign.Therefore,these
classesautomaticallyavoidexplicitsynchronization.

Can You Avoid Synchronization?


Developersofthreadedprogramsareoftenparanoidaboutsynchronization.Therearemanyhorrorstoriesaboutprogramsthatperformedpoorlybecauseof
excessiveorincorrectsynchronization.Ifthereisalotofcontentionforaparticularlock,acquiringthelockbecomesanexpensiveoperationfortworeasons:
Thecodepathinmanyvirtualmachineimplementationsisdifferentforacquiringcontendedanduncontendedlocks.Acquiringacontendedlockrequires
executingmorecodeatthevirtualmachinelevel.Theconverseofthisstatementisalsotrue,however:acquiringanuncontendedlockisafairly
inexpensiveoperation.
Beforeacontendedlockcanbyacquired,itscurrentholdermustreleaseit.Athreadthatwantstoacquireacontendedlockmustalwayswaitforthelock
tobereleased.

CO NT ENDEDANDUNCO NT ENDEDLO CKS


Thetermscontendedanduncontendedrefertohowmanythreadsareoperatingonaparticularlock.Alockthatisnotheldbyanythreadisan
uncontendedlock:thefirstthreadthatattemptstoacquireitimmediatelysucceeds.
Whenathreadattemptstoacquirealockthatisalreadyheldbyanotherthread,thelockbecomesacontendedlock.Acontendedlockhasatleast
onethreadwaitingforititmayhavemanymore.Notethatacontendedlockbecomesanuncontendedonewhenthreadsarenolongerwaitingto
acquireit.

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.

The Eect of Registers

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.

The Eect of Reordering Statements


Developersoftenhopethattheycanavoidsynchronizationbydependingontheorderofexecutionofstatements.Supposethatwedecidetokeeptrackofthe

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.

Overview of the Atomic Classes


Fourbasicatomictypes,implementedbytheAtomicInteger,AtomicLong,AtomicBoolean,andAtomicReferenceclasses,handleintegers,
longs,booleans,andobjects,respectively.Alltheseclassesprovidetwoconstructors.Thedefaultconstructorinitializestheobjectwithavalueofzero,false,
ornull,dependingonthedatatype.Theotherconstructorcreatesthevariablewithaninitialvaluethatisspecifiedbytheprogrammer.Theset()andget()
methodsprovidefunctionalitythatisalreadyavailablewithvolatilevariables:theabilitytoatomicallysetorgetthevalue.Theget()andset()methods
alsoensurethatthedataisreadfromorwrittentomainmemory.
ThegetAndSet()methodoftheseclassesprovidesnewfunctionality.Thismethodatomicallysetsthevariabletoanewvaluewhilereturningtheprevious
value,allwithoutacquiringanysynchronizationlocks.Understandthatitisnotpossibletosimulatethisfunctionalityatomicallyusingonlygetandset
operatorsattheJavalevelwithouttheuseofsynchronization.Ifitisnotpossible,thenhowisitimplemented?Thisfunctionalityisaccomplishedthroughthe
useofnativemethodsnotaccessibletouserlevelJavaprograms.Youcouldwriteyourownnativemethodstoaccomplishthis,buttheplatformspecific
issuesarefairlydaunting.Furthermore,sincetheatomicclassesarecoreclassesinJava,theydon'thavethesecurityissuesrelatedtouserdefinednative
methods.
ThecompareAndSet()andweakCompareAndSet()methodsareconditionalmodifiermethods.Bothofthesemethodstaketwoargumentsthevalue
thedataisexpectedtohavewhenthemethodstarts,andanewvaluetosetthedatato.Themethodssetthevariabletothenewvalueonlyifthevariablehas
theexpectedvalue.Ifthecurrentvalueisnotequaltotheexpectedvalue,thevariableisnotchangedandthemethodreturnsfalse.Abooleanvalueoftrueis
returnedifthecurrentvalueisequaltotheexpectedvalue,inwhichcase,thevalueisalsosettothenewvalue.Theweakformofthismethodisbasicallythe
same,butwithonelessguarantee:ifthevaluereturnedbythismethodisfalse,thevariablehasnotbeenupdated,butthatdoesnotmeanthattheexisting
valueisnottheexpectedvalue.Thismethodcanfailtoupdatethevalueregardlessofwhethertheinitialvalueistheexpectedvalue.
TheAtomicIntegerandAtomicLongclassesprovideadditionalmethodstosupportintegerandlongdatatypes.Interestingly,thesemethodsareall
conveniencemethodsimplementedinternallyusingthecompareandsetfunctionalityprovided.However,thesemethodsareimportantandfrequentlyused.
TheincrementAndGet(),decrementAndGet(),getAndIncrement(),andgetAndDecrement()methodsprovidethefunctionalityofthepre
increment,predecrement,postincrement,andpostdecrementoperators.TheyareneededbecauseJava'sincrementanddecrementoperatorsaresyntactic
sugarformultipleloadandstoreoperationstheseoperationsarenotatomicwithvolatilevariables.Usinganatomicclassallowsyoutotreattheoperations
atomically.
TheaddAndGet()andgetAndAdd()methodsprovidethepreandpostoperatorsfortheadditionofaspecificvalue(thedeltavalue).Thesemethods
allowtheprogramtoincrementordecrementavariablebyanarbitraryvalueincludinganegativevalue,makingasubtractioncounterparttothesemethods
unnecessary.
Doestheatomicpackagesupportmorecomplexvariabletypes?Yesandno.Thereiscurrentlynoimplementationofatomiccharacterorfloatingpoint
variables.YoucanuseanAtomicIntegertoholdacharacter,butusingatomicfloatingpointnumbersrequiresatomicallymanagedobjectswithreadonly
floatingpointvalues.Weexaminethatcaselaterinthischapter.
Someclassessupportarraysandvariablesthatarealreadypartofotherobjects.However,noextrafunctionalityisprovidedbytheseclasses,sosupportof

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.

Using the Atomic Classes


Aswementioned,itispossible(intheory)toimplementeveryprogramorclassthatwehaveimplementedsofarusingonlyatomicvariables.Intruth,itis
notthatsimple.Theatomicclassesarenotadirectreplacementofthesynchronizationtoolsusingthemmayrequireacomplexredesignoftheprogram,
eveninsomesimpleclasses.Tounderstandthisbetter,let'smodifyourScoreLabelclass
packagejavathreads.examples.ch05.example1;

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.

Notifications and Atomic Variables

Notifications and Atomic Variables


Isitpossibletouseatomicvariablesifwealsoneedthefunctionalityofconditionvariables?Implementingconditionvariablefunctionalityusingatomic
variablesispossiblebutnotnecessarilyefficient.Synchronizationandthewaitandnotifymechanismisimplementedbycontrollingthethreadstates.
Threadsareblockedfromrunningiftheyareunabletoacquirethelock,andtheyareplacedintoawaitstateuntilaparticularconditionoccurs.Atomic
variablesdonotblockthreadsfromrunning.Infact,codeexecutedbyunsynchronizedthreadsmayhavetobeplacedintoaloopformorecomplex
operationsinordertoretryattemptsthatfail.Inotherwords,itispossibletoimplementtheconditionvariablefunctionalityusingatomicvariables,butthreads
willbespinningastheywaitforthedesiredcondition.
Thisdoesnotmeanthatyoushouldavoidatomicvariablesifyouneedconditionvariablefunctionality.Onceagain,abalancemustbefound.Itispossibleto
useatomicvariablesforportionsofaprogramthatdonotentailnotificationsandtousesynchronizationelsewhere.Itispossibletoimplementallofa
programwithatomicvariablesanduseaseparatelibrarytosendsuchnotificationsalibrarythatisinternallyusingconditionvariables.Ofcourse,insome
situations,itisnotaproblemtoallowthethreadstospinwhilewaiting.
Thislastalternativeisthecasewithourtypinggame.First,onlytwothreadstheanimationcomponentthreadandthecharactergeneratorthreadneedto
waitforacondition.Second,thewaitingprocessoccursonlywhenthegameisstopped.Theprogramisalreadywaitingbetweenframesoftheanimation
usingthissameloopandintervaltowaitfortheusertorestartthegamedoesnotaddasignificantperformancepenalty.Third,waitingforabout100
milliseconds(theintervalperiodbetweenframesoftheanimation)shouldnotbenoticeabletotheuserwhentheStartbuttonispressedanyuserwhonotices
thatdelaywillalsonoticethedelaysintheanimationitself.
Hereisanimplementationofouranimationcomponentusingonlyatomicvariablesitspinswhiletheuserhasstoppedthegame.Asimilarimplementation
oftherandomcharactergeneratorisavailableintheonlineexamples.
packagejavathreads.examples.ch05.example2;

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.

Summary of Atomic Variable Usage


Theseexamplesshowanumberofcanonicalusesofatomicvariableswe'veusedmanytechniquestoextendtheatomicoperationsprovidedbyatomic
variables.Hereisasummaryofthosetechniques.

O PT IMIST ICSYNCHRO NIZ AT IO N


What'shappeninginourexampleswithatomicvariablesisthatthereisnofreelunch:thecodeavoidssynchronization,butitpaysapotentialpenaltyin
theamountofworkitperforms.Youcanthinkofthisas"optimisticsynchronization"(tomodifyatermfromdatabasemanagement):thecodegrabs
thevalueoftheprotectedvariableassumingthatnooneelseismodifyingitatthemoment.Thecodethencalculatesanewvalueforthevariableand
attemptstoupdatethevariable.Ifanotherthreadmodifiedthevariableinthemeantime,theupdatefailsandthecodemustrestartitsprocedure(using
thenewlymodifiedvalueofthevariable).
Theatomicclassesusethistechniqueinternallyintheirimplementation,andweusethistechniqueinourexampleswhenwehavemultipleoperations
onanatomicvariable.

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.

COMPARE AND SET


Comparingandsettingistheabilitytosetavalueatomicallyonlyifthecurrentvalueisanexpectedvalue.ThecompareAndSet()methodhandlesthis
case.Thisimportantmethodprovidestheabilitytohaveconditionalsupportatanatomiclevel.Thisbasicfunctionalitycanevenbeusedtoimplementthe
synchronizationabilityprovidedbymutexes.

Whatifthecomparisonismorecomplex?Whatifthecomparisonisdependentonthepreviousorexternalvalues?Thiscasecanbehandledasbeforeby
placingtheget()andthecompareAndSet()methodsinaloop.Theget()methodisusedtogetthepreviousvalue,whichcanbeusedeitherfor
comparisonorjusttoallowanatomicexchange.Thecomplexcomparisonisusedtoseeiftheoperationshouldproceed.ThecompareAndSet()method
isthenusedtosetthevalueifthecurrentvaluehasnotchanged.Thewholeoperationisretriediftheoperationfails.Asbefore,thewholeoperationis
consideredatomicbecausethedataischangedatomicallyandchangedonlyifitmatchesthevalueatthestartoftheoperation.

ADVANCED ATOMIC DATA TYPES


Althoughthelistofdatatypesforwhichatomicclassesareavailableisprettyextensive,itisnotcomplete.Theatomicpackagedoesn'tsupportcharacterand
floatingpointtypes.Whileitdoessupportgenericobjecttypes,itdoesn'tsupporttheoperationsneededformorecomplextypesofobjects,suchasstrings.
However,wecanimplementatomicsupportforanynewtypebysimplyencapsulatingthedatatypeintoareadonlydataobject.Thedataobjectcanthenbe
changedatomicallybychangingtheatomicreferencetoanewdataobject.Thisworksonlyifthevaluesembeddedwithinthedataobjectarenotchangedin
anyway.Anychangetothedataobjectmustbeaccomplishedonlybychangingthereferencetoadifferentobjectthepreviousobject'svaluesarenot
changed.Allvaluesencapsulatedbythedataobject,directlyandindirectly,mustbereadonlyforthistechniquetowork.
Asaresult,itmaynotbepossibletochangeafloatingpointvalueatomically,butitispossibletochangeanobjectreferenceatomicallytoadifferentfloating
pointvalue.Aslongasthefloatingpointvaluesarereadonly,thistechniqueisthreadsafe.Withthisinmind,wecanimplementanatomicclassforfloating
pointvalues:

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.

BULK DATA MODIFICATION


Inourpreviousexamples,wehavesetonlyindividualvariablesatomicallywehaven'tsetgroupsofvariablesatomically.Inthosecaseswherewesetmore
thanonevariable,wewerenotconcernedthattheybesetatomicallyasagroup.However,atomicallysettingagroupofvariablescanbedonebycreatingan
objectthatencapsulatesthevaluesthatcanbechangedthevaluescanthenbechangedsimultaneouslybyatomicallychangingtheatomicreferencetothe
values.ThisworksexactlyliketheAtomicDoubleclass.
Onceagain,thisworksonlyifthevaluesarenotdirectlychangedinanyway.Anychangetothedataobjectisaccomplishedbychangingthereferencetoa
differentobjectthepreviousobject'svaluesmustnotbechanged.Allvalues,encapsulatedeitherdirectlyandindirectly,mustbereadonlyforthistechnique
towork.
Hereisanatomicclassthatprotectstwovariables:ascoreandacharactervariable.Usingthisclass,weareabletodevelopatypinggamethatmodifiesboth
thescoreandcharactervariablesatomically:

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.

Thread Local Variables


Anythreadcan,atanytime,defineathreadlocalvariablethatisprivatetothatparticularthread.Otherthreadsthatdefinethesamevariablecreatetheirown
copyofthevariable.Thismeansthatthreadlocalvariablescannotbeusedtosharestatebetweenthreadschangestothevariableinonethreadareprivateto
thatthreadandnotreflectedinthecopiesheldbyotherthreads.Butitalsomeansthataccesstothevariableneedneverbesynchronizedsinceit'simpossible
formultiplethreadstoaccessthevariable.Threadlocalvariableshaveotheruses,ofcourse,buttheirmostcommonuseistoallowmultiplethreadstocache
theirowndataratherthancontendforsynchronizationlocksaroundshareddata.
Athreadlocalvariableismodeledbythejava.lang.ThreadLocalclass:

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.

Inheritable Thread Local Variables


Valuesstoredbythreadsinthreadlocalvariablesareunrelated.Whenanewthreadiscreated,itgetsanewcopyofthethreadlocalvariable,andthevalueof
thatvariableiswhat'sreturnedbytheinitialValue()methodofthethreadlocalsubclass.
AnalternativetothisideaistheInheritableThreadLocalclass:
packagejava.lang;
publicclassInheritableThreadLocalextendsThreadLocal{
protectedObjectchildValue(ObjectparentValue);
}

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).

Chapter6.Advanced Synchronization Topics


Inthischapter,welookatsomeofthemoreadvancedissuesrelatedtodatasynchronizationspecifically,timingissuesrelatedtodatasynchronization.
WhenyouwriteaJavaprogramthatmakesuseofseveralthreads,issuesrelatedtodatasynchronizationarethosemostlikelytocreatedifficultiesinthe
designoftheprogram,anderrorsindatasynchronizationareoftenthemostdifficulttodetectsincetheydependoneventshappeninginaspecificorder.Often
anerrorindatasynchronizationcanbemaskedinthecodebytimingdependencies.Youmaynoticesomesortofdatacorruptioninanormalrunofyour
program,butwhenyouruntheprograminadebuggeroraddsomedebuggingstatementstothecode,thetimingoftheprogramiscompletelychanged,and
thedatasynchronizationerrornolongeroccurs.
Theseissuescan'tbesimplysolved.Instead,developersneedtodesigntheirprogramswiththeseissuesinmind.Developersneedtounderstandwhatthe
differentthreadingissuesare:whatarethecauses,whattheyshouldlookfor,andthetechniquestheyshouldusetoavoidandmitigatethem.Developers
shouldalsoconsiderusinghigherlevelsynchronizationtoolstoolsthatprovidethetypeofsynchronizationneededbytheprogramandthatareknowntobe
threadsafe.Weexaminebothoftheseideasinthischapter.

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.

Synchronization Classes Added in J2SE 5.0


Youprobablynoticedastrongpatternwhilereadingthislistofterms:beginningwithJ2SE5.0,almostallthesethingsareincludedinthecoreJavalibraries.
We'lltakeabrieflookintotheseJ2SE5.0classes.

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.

VIRT UALMACHINE LEVELDEADLO CKDET ECT IO N


Incertaincases,thevirtualmachinecandetectthattwothreadsaredeadlocked.It'spossibletoobtainastacktraceforallactivethreadsinthevirtual
machinethroughaplatformspecificoperation.OnSolaris,Linux,andotherUnixsystems,sendingthevirtualmachinea3signal(viathekill
command)producesthatoutput.OnWindowssystems,enteringCtrlBreakproducesthestackoutput.
Iftwoormorethreadsarewaitingforeachother'slocks,thevirtualmachinedetectsthisandprintsoutthatinformationinthethreaddump.However,
eventhoughthevirtualmachinehasdetectedthedeadlock,itdoesnottakeanystepstoresolveit.
Thevirtualmachinecannotdetectotherkindsofdeadlock,suchasthefirstcaseweexaminedinChapter3.Inthatexample,thedeadlockoccurred
becausetherun()methodneverallowedanyothermethodtograbthesynchronizationlock.Thatkindofapplicationleveldeadlockisimpossiblefor
thevirtualmachinetodetect.

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.

Deadlock and Automatic Lock Releases


ThereareafewmoreconcernsaboutdeadlockwhenusingtheLockinterface(oranylockingmechanismthatisnottheJavasynchronizedkeyword).
ThefirstisillustratedbyhowwehaveusedtheLockclassineveryexampleuptothispoint.OurresetScore()methodcanbeeasierwritten(and
understood)asfollows:

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.

Preventing Deadlock with Timeouts


SincetheLockinterfaceprovidesoptionsforwhenalockcan'tbegrabbedcanweusethoseoptionstopreventdeadlock?Absolutely.Anotherwayto
preventdeadlockisnottowaitforthelockoratleast,toplacerestrictionsonthewaitingperiod.ByusingthetryLock()methodtoprovidealternativesin
thealgorithm,thechancesofdeadlockcanbegreatlymitigated.Forexample,ifweneedaresourcebuthaveanalternate(maybeslower)resourceavailable,
usingthealternateresourceallowsustocompletetheoperationandultimatelyfreeanyotherlockswemaybeholding.Alternatively,ifweareunableto
obtainthelockwithinatimelimit,perhapswecancleanupourstateincludingreleasingthelockswearecurrentlyholdingandallowotherconflicting
threadstofinishupandfreetheirlocks.
Unfortunately,usingexplicitlocksinthisfashionismorecomplexthanusingalockhierarchy.Todevelopalockhierarchy,wesimplyhavetofigureoutthe
orderinwhichthelocksmustbeobtained.Tousetimeouts,weneedtodesigntheapplicationtoallowalternativepathstocompletion,orthecapabilityto
"undo"operationsforalater"redo."Theadvantagetotimeoutsisthattherecanbeagreaterdegreeofparallelism.Weareactuallydesigningmultiple
pathwaystocompletiontoavoiddeadlockinsteadofplacingrestrictionsonthealgorithminordertoavoiddeadlock.
YoumustdecidewhetherthesetypesofbenefitsoutweightheaddedcomplexityofthecodewhenyoudesignyourJavaprogram.Ifyoustartbycreatinga
lockhierarchy,you'llhavesimplercodeatthepossibleexpenseofthelossofsomeparallelism.Wethinkitiseasiertowritethesimplercodefirstandthen
addresstheparallelismproblemsiftheybecomeaperformancebottleneck.

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.

Lock Starvation and Reader/Writer Locks


Generally,reader/writerlocksareusedwhentherearemanymorereadersthanwritersreadersalsotendtoholdontothelockforalongerperiodoftimethan
theywouldasimplelock.Thisiswhythereader/writerlockisneededtosharedataaccessduringthelongperiodsoftimewhenonlyreadingisneeded.
Unfortunately,readerscan'tjustgrabthelockifthelockisheldforreadingbyanotherthread.Ifmanyreaderswereholdingthelock,itwouldbepossiblefor
manymorereaderstograbthelockbeforethefirstsetofreadersfinished.Manymorereaderscouldthenobtainthelockbeforethesecondsetofreaders
finished.Thiswouldpreventthewriterfromeverobtainingthelock.
Tosolvethis,thereader/writerlockdoesnotgrantthereadlocktoanewthreadifthereisawriterwaitingforthelock.Insteaditplacesthereaderintoawait
stateuntilthefirstsetofreadersfreesthelock.Oncethefirstsetofreadershavecompleted,thefirstwriterisgivenexclusiveaccesstothelock.Whenthat
writercompletes,theReentrantReadWriteLockclassconsultsitsfairnessflagtoseewhattodonext.Ifthefairnessflagistrue,thethreadwaiting
longestwhetherareaderorawriterisgrantedthelock(andmultiplereaderscangetthelockinthiscase).Ifthefairnessflagisfalse,locksaregranted
arbitrarily.

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"/>

Chapter7.Threads and Swing


TheSwingclassesinJavaarenotthreadsafeifyouaccessaSwingobjectfrommultiplethreads,yourunthechanceofdatacorruption,hungGUIs,and
otherundesirableeffects.Todealwiththissituation,youmustmakesurethatyouaccessSwingobjectsonlyfromoneparticularthread.Wesawsome
examplesofthisinpreviouschaptersthischapterexplainsthedetailsofhowthreadsinteractwithSwing.Thegeneralprinciplesofthischapterapplytoother
threadunsafeobjects:youcanhandleanythreadunsafeclassbyaccessingitinasinglethreadinmuchthesamewayasSwingobjectsmustbeaccessed
fromaspecialthread.
We'llstartwithageneraldiscussionofthethreadsthatSwingcreatesautomaticallyforyou,andthenwe'llseehowyourownthreadscaninteractwiththose
threadssafely.Indoingso,we'll(finally)explainthelastpiecesofourtypingprogram.
Ifyou'reinterestedinthegeneralcaseofhowtodealwithasetofclassesthatarenotthreadsafe,youcanreadthroughthefirstsectionofthischapterforthe
theoryofhowthisishandled,thenreviewourexampleinChapter10toseethetheoryputintopractice.

Swing Threading Restrictions


AGUIprogramhasseveralthreads.Oneofthesethreadsiscalledtheeventdispatchingthread.Thisthreadexecutesalltheeventrelatedcallbacksofyour
program(e.g.,theactionPerformed()andkeyPressed()methodsinourtypingtestprogram).AccesstoallSwingobjectsmustoccurfromthis
thread.
ThereasonforthisisthatSwingobjectshavecomplexinnerstatethatSwingitselfdoesnotsynchronizeaccessto.AJSliderobject,forexample,hasa
singlevaluethatindicatesthepositionoftheslider.Iftheuserisinthemiddleofchangingthepositionoftheslider,thatvaluemaybeinanintermediateor
indeterminatestateallofthatprocessingoccursontheeventdispatchingthread.Asecondthreadthatattemptstoreadthevalueoftheslidercannotreadthat
valuedirectlysincebydoingsothethreadmayreadthevaluewhilethevalueisinitsintermediatestate.Therefore,thesecondthreadmustarrangeforthe
eventdispatchingthreadtoreadthevalueandpassthevaluebacktothethread.
Notethatit'snotenoughforoursecondthreadsimplytosynchronizeaccesstotheJSliderobject.TheinternalSwingmechanismsaren'tsynchronizing
access,sothetwothreadsstillsimultaneouslyaccesstheinternalstateoftheslider.Rememberthatlocksarecooperative:ifallthreadsdonotattemptto
acquirethelock,raceconditionscanstilloccur.
Itmayseemlikethisrestrictionisoverkill:thevalueofaJSliderisasinglevariableandcouldsimplybemadevolatile.Actually,that'snotthecase.
ThevalueofthingswithinSwingcomponentscanbeverycomplex.ManySwingcomponentsfollowamodelviewcontrollerdesignpattern,andaccessing
thosecomponentsfromonethreadwhilethemodelisbeingupdatedontheeventdispatchingthreadwouldbeverydangerous.EventhesimplestofSwing
componentscontaincomplexstateit'sneveracceptabletocallanyoftheirmethodsfromathreadotherthantheeventdispatchingthread.
Consequently,allcallstoSwingobjectsmustbemadeontheeventdispatchingthread.That'sthethreadthatSwingusesinternallytochangethestateofits
objectsaslongasyoumakecallstoSwingobjectsfromthatthread,noraceconditioncanoccur.Fourexceptionstothisruleare:
Swingobjectsthathavenotbeendisplayedcanbecreatedandmanipulatedbyanythread.ThatmeansyoucancreateyourGUIobjectsinanythreadbut
oncethey'vebeendisplayed,theycanbeaccessedonlyontheeventdispatchingthread.AGUIobjectisdisplayedwhentheshow()methodofitsparent
frameiscalled.
Therepaint()methodcanbecalledfromanythread.
TheinvokeLater()methodcanbecalledfromanythread.
TheinvokeAndWait()methodcanbecalledfromanythreadotherthantheeventdispatchingthread.

Processing on the Event-Dispatching Thread


Aswementioned,alltheeventcallbacksofyourprogramoccurontheeventdispatchingthread.Thisisgoodnewssinceitmeansthatmostofthecodethat
needstoaccessSwingcomponentsisautomaticallycalledontheeventdispatchingthread.
Inoursampletypingprogram,weaccessSwingcomponentsfromthesemethods:

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.

Using invokeLater( ) and invokeAndWait( )


IntheCharacterDisplayCanvasclass,wewereabletoworkaroundSwing'sthreadingrestrictionsbecauseallthecallsthatmanipulatedSwingobjects
couldgointoaneventcallbackmethod(thepaintComponent()method).That'snotalwaysconvenient(orevenpossible).SoSwingprovidesanother
mechanismthatallowsyoutoruncodeontheeventdispatchingthread:theinvokeLater()andinvokeAndWait()methods.

WHICHINVO KELAT ER( ) ?WHICHINVO KEANDWAIT ( ) ?


JavadefinestheinvokeLater()andinvokeAndWait()methodsintwodifferentclasses:javax.swing.SwingUtilitiesand
java.awt.EventQueue.Thisisduetohistoricalreasons,andyoucanusewhicheverclassyoulike.Themethodsareidentical.TheinvokeLater()
methodoftheSwingUtilitiesclasssimplycallstheinvokeLater()methodoftheEventQueueclass,sotheyarefunctionallyidenticalthesameis
trueofthetwoinvokeAndWait()methods.

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){}
}

Long-Running Event Callbacks


There'sanothercasewhenSwingprogramsandthreadsinteract:alongrunningeventcallback.Whileaneventcallbackisexecuting,therestoftheGUIis
unresponsive.Ifthishappensforalongperiodoftime,itcanbeveryfrustratingtousers,whooftenassumethattheprogramhashung.It'sfarbetterto
executethelongrunningtaskinaseparatethread,providingGUIfeedbackasappropriate.
Thistaskcanbeaccomplishedinafewways.Bynow,youshouldbefamiliarenoughwiththreadprogrammingtospawnyourownthreadandexecutethe
task,andthat'softenthesimplestroutetotake.AutilityclasscalledtheSwingWorkerclass,availableonSun'shttp://java.sun.com(http://java.sun.com)web
site,canhandlemanyofthethreadingdetailsforyou(but,intheend,itisnotreallyanyeasierthanspawningyourownthread).
Ifyou'regoingtohavealotoftaskslikethis,though,theeasiestthingtodoisuseathreadpoolorataskscheduler.Ifyouhavealotoftaskstoexecutein
parallel,youcanuseathreadpool(seeChapter10).Ifyouhaveonlyasingletasktoexecuteeverynowandthen,youcanuseataskscheduler(seeChapter
11).
Here'sanexampleofhowtotakethefirstpathandsetupathreadinalongrunningcallback.Supposethatinourtypetester,thestartmethodmustlogintoa
serverinordertogetthedataitistodisplay.Youwanttoperformthatoperationinaseparatethreadbecauseitmaytakealongtime,duringwhichyoudon't
wanttheGUItobeunresponsive.Infact,youwanttogivetheuseranoptiontocancelthatoperationincasecommunicatingwiththeservertakestoolong.
Here'saclassthatsimulatesconnectingtotheserver.Whileit'satit,theframedisplayssomeprogressmessages:
packagejavathreads.examples.ch07.example3;

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

Chapter8.Threads and Collection Classes


Inthischapter,we'lllookathowthreadsinteractwiththecollectionclassesprovidedbyJava.We'llexaminesomesynchronizationissuesandhowtheyaffect
ourchoiceandusageofcollectionclasses.
Thecollectionclassescomprisemanyoftheclassesinthejava.utilpackage(and,inJ2SE5.0,someoftheclassesinthejava.util.concurrent
package).Collectionclassesareusedtostoreobjectsinsomedatastructure:ahashtable,anarray,aqueue,andsoon.CollectionclassesinteractwithJava
threadsinafewareas:
Collectionclassesmayormaynotbethreadsafe,sothreadsthatusethoseclassesmustunderstandtheirsynchronizationrequirements.
Notallcollectionshavethesameperformancewithregardtothreadsynchronization,sothreadsthatusethemmustunderstandtheconditionsinwhich
theycanbeusedoptimally.
Newercollectionclassesautomaticallyprovidesomethreadingsemantics(suchasusingthreadnotificationwhentheirdatachanges).
Threadscommonlyusecollectionclassestosharedata.
Webeginthischapterwithanoverviewofthecollectionclassestheoverviewaddressesthethreadsafetyofthevariousclasses.Next,weshowhowsomeof
thenewercollectionclassesinteractwiththreads.Andfinally,weshowacommondesignpatterninwhichmultiplethreadsusethecollections:theproducer
consumermodel.

Overview of Collection Classes


Inthebeginning,Javaprovidedonlyafewcollectionclasses.Infact,inthefirstversionofJava,theseclassesweren'tevenreferredtoascollectionclasses
theyweresimplyutilityclassesthatJavaprovided.Forthemostpart,theseclasseswereallthreadsafetheearlycollectionclassesweredesignedtoprevent
developersfrominadvertentlycorruptingthedatastructuresbyusingthemindifferentthreadswithoutappropriatedatasynchronization.
JDK1.2introducedtheformalideaofcollectionclasses.ThefewexistingdatacollectionclassesfromJDK1.0and1.1wereintegratedintothisframework,
whichwasexpandedtoincludenewclassesandnewinterfaces.Definingthecollectionclassesintermsofinterfacesmadeitpossibletowriteprogramsthat
couldusedifferentcollectionimplementationsatruntime.
ThecontroversialchangeintroducedinJDK1.2isthatmostofthecollectionclassesarenow,bydefault,notthreadsafe.Threadsafeversionsoftheclasses
exist,butthedecisionwasmadetoallowthedevelopertomanagethethreadsafetyoftheclasses.Twofactorsinformthisdecision:theperformanceof
synchronizationandtherequirementsofalgorithmsthatusethecollection.We'llhavemoretosayonthoseissuesinthenextsection.JDK1.3and1.4added
someminorextensionstothesecollectionclasses.
J2SE5.0introducesanumberofnewcollectionclasses.Someoftheseclassesaresimpleextensionstotheexistingcollectionsframework,butmanyof
themhavetwodistinguishingfeatures.First,theirinternalimplementationmakesheavyuseofthenewJ2SE5.0synchronizationtools(inparticular,atomic
variables).Second,mostoftheseclassesaredesignedtobeusedbymultiplethreadsandsupporttheideaofthreadnotificationwhendatainthecollection
becomesavailable.

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.

Threadsafe Collection Classes


Onlyafewcollectionclassesarethreadsafe.Aswe'llseelater,beingthreadsafedoesnotnecessarilymeanthatyoucansafelyusethemineverymultithreaded
programprogramsmuststillbedesignedinafashionthatallowsthecollectiontobeusedbymultiplethreads.Herearesomeofthemorecommon
threadsafecollectionclasses:
java.util.Vector(aList)
Asimplearray,allowingindexbasedoperationsandrandominsertionanddeletion.
java.util.Stack(aList)
TheStackclassextendstheVectorclasstoprovidetheabilitytotreatthevectorasastack.Objectscanbepushedontothestackorpoppedfromthe
stack,providingaLIFOordering(however,thisclassdoesnotimplementtheQueueinterface).

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.

Thread-Unsafe Collection Classes


Themajorityofcollectionclassesarenotthreadsafe.Whenusedinmultithreadedprograms,accesstothemmustalwaysbecontrolledbysome
synchronization.Thissynchronizationcanbeaccomplishedeitherbyusinga"wrapper"classthatsynchronizeseveryaccessoperation(usingthe
Collectionsclass,whichwe'llshowlater)orbyusingexplicitsynchronization:
java.util.BitSet
Abitsetstoresanarrayofboolean(1bit)values.Thesizeofthearraycangrowatwill.ABitSetsavesspacecomparedtoanarrayofbooleanssince
thebitsetcanstoremultiplevaluesinonelongvariable.Despiteitsname,itdoesnotimplementtheSetinterface.
java.util.HashSet(aSet)
Aclassthatimplementsanunorderedsetcollection.
java.util.TreeSet(aSortedSet)
Aclassthatimplementsasorted(andordered)setcollection.

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).

Thread-Notification Collection Classes


Anumberofclassesinthejava.util.concurrentpackagearedesignedtoprovidethreadnotificationwhentheircontentschange.Theyareinherently
threadsafesincetheyareexpectedtobeusedbymultiplethreadssimultaneously.Theysimplifyusageofcollectionsbyprovidingsemanticstohandleoutof
spaceandoutofdataconditionswithinthecollection.We'llseeexamplesofthislaterinthechapter.

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.

Synchronization and Collection Classes


Whenwritingamultithreadedprogram,themostimportantquestionwhenusingacollectionclassishowtomanageitssynchronization.Synchronizationcan
bemanagedbythecollectionclassitselformanagedexplicitlyinyourprogramcode.Intheexamplesinthissection,we'llexplorebothoftheseoptions.

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.)

Iterators and Enumerations


Manysituationscallforusingeachelementofacollection.Suchisthecaseinourexample.WecalledthetoArray()method,whichreturnsanarray
containingeveryelementinthevector.TheVectorandHashtableclassesalsohavemethodsthatreturnajava.util.Enumerationobjectthat
containseveryelementinthecollection.Moregenerally,allcollectionclassesimplementoneormoremethodsthatreturnajava.util.Iteratorobject.
Theiteratoralsocontainseveryelementinthecollection.
Eachofthesetechniquespresentsspecialsynchronizationconcerns.We'vealreadyseenthatloopingthroughthearrayreturnedbythetoArray()method
canleadtoasituationwherewe'reaccessinganelementinthearraythatnolongerappearsinthecollection.Thatmayormaynotbeaproblemforyour
programifitisaproblem,thesolutionistosynchronizeaccessaroundtheloopthatusesthearray.
Enumerationobjectsaredifficulttousewithoutexplicitsynchronization.Theenumerationkeepsstateinformationaboutthecollectionifthecollectionis
modifiedwhiletheenumerationisactive,theenumerationmaybecomeconfused.Theenumerationfailsinsomerandomway,possiblythroughan
unexpectedruntimeexception(e.g.,aNullPointerException).
Touseanenumerationofacollectionthatmayalsobeusedbymultiplethreads,youshouldsynchronizeonthecollectionobjectitself:

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.

The Producer/Consumer Pattern


Oneofthemorecommonpatternsinthreadedprogrammingistheproducer/consumerpattern.Theideaistoprocessdataasynchronouslybypartitioning
requestsamongdifferentgroupsofthreads.Theproducerisathread(orgroupofthreads)thatgeneratesrequests(ordata)tobeprocessed.Theconsumeris
athread(orgroupofthreads)thattakesthoserequests(ordata)andactsuponthem.Thispatternprovidesacleanseparationthatallowsforbetterthread
designandmakesdevelopmentanddebuggingeasier.ThispatternisshowninFigure81.

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.

Using the Collection Classes


So,whicharethebestcollectionstouse?Obviously,nosingleanswerfitsallcases.However,herearesomegeneralsuggestions.Byadheringtothese
suggestions,wecannarrowthechoiceofwhichcollectiontouse.
Whenworkingwithcollectionclasses,workthroughinterfaces
AswithallJavaprogramming,interfacesisolateimplementationdetails.Byusinginterfaces,theprogrammercaneasilyrefactoraprogramtousea
differentcollectionimplementationbychangingonlytheinitializationcode.
Thereislittleperformancebenefitinusinganonsynchronizedcollection
Thismaybesurprisingtomanydevelopersforanunderstandingoftheperformanceissuesaroundlockacquisition,seeChapter14.Inbrief,
performanceissueswithlockacquisitionsoccuronlywhenthereiscontentionforthelock.However,anonsynchronizedcollectionshouldhaveno
contentionforthelock.Ifthereiscontention,havingraceconditionsisamoreproblematicissuethanperformance.
Foralgorithmswithalotofcontention,considerusingtheconcurrentcollections
Theset,hashmap,andlistcollectionsthatwereaddedinJ2SE5.0arehighlyoptimized.Ifaprogram'salgorithmfitsintooneoftheseinterfaces,
considerchoosingaJ2SE5.0collectionoverasynchronizedversionofaJDK1.2collection.Theconcurrentcollectionsaremuchbetteroptimizedfor
multithreadedaccess.
Forproducer/consumerbasedprograms,considerusingaqueueasthecollection
Queuesarebestfortheproducer/consumermodelformanyreasons.First,queuesprovideanorderingofrequests,preventingdatastarvation.Second,
queuesarehighlyoptimized,havingminimalsynchronization,atomicaccesses,andevensafeparallelaccessinmanycases.Withthesecollections,a
hugenumberofthreadscanworkinparallelwithlittlebottleneckingatthequeue'saccesspoints.
Whenpossible,trytominimizetheuseofexplicitsynchronization
Iteratorsandothersupportmethodsthatrequiretranversalofanentirecollectionmayneedmoresynchronizationthanthecollectionprovidesalone.This
canbeaproblemwhenmanythreadsareinvolved.
Limityouruseofiteratorsfromthecopyonwritecollections
First,usetheseclassesonlywhenthenumberofelementsinthecollectionissmall.Thisisbecauseofthetimeandsizerequirementsofthecopyon
writeoperation.Second,yourprogrammustnotrequirethatthecollectionhavethemostuptodateinformation.Theiteratorcontainsonlythe
informationofthecollectionatthetimethatitiscreated.
Considerusingmultiplecollections
Whilesomeofthesecollectionshaveminimalsynchronization,thesesynchronizationperiodscanstillbeanissuewhenmanythreadsareinvolved.
Considerhavinganalgorithmthatusessegmentedcollectionsinsteadofagenericimplementationinwhichallthreadsusethesamecollection.
Thereislittledifferencebetweenasetandamap
Theoretically,asetandamaparedifferentinanumberofways,butintermsofimplementation,thereislittledifference.Manyofthesetcollectionsare
justimplementedbyusingthemapcollection.Thismeansthatthechoiceisnotactuallyachoice:anitemstoredinasetismerelystoredasakeyina
map.

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.

An Overview of Thread Scheduling


We'llstartbylookingatthebasicprinciplesofhowthreadsarescheduled.Anyparticularvirtualmachine(andunderlyingoperatingsystem)maynotfollow
theseprinciplesexactly,buttheprinciplesformthebasisforourunderstandingofthreadscheduling.
Let'sstartbylookingatanexamplewithsomeCPUintensivethreads.Inthisandsubsequentchapters,we'llconsumeCPUresourceswitharecursive
Fibonaccinumbergenerator,whichhastheadvantage(forourpurposes)ofbeinganelegantandveryslowprogram:

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.

The Scheduling Process

The Scheduling Process


Let'sexaminehowtheschedulingprocessworksinalittlemoredetail.Ataconceptuallevel,everythreadintheJavavirtualmachinecanbeinoneoffour
states:

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.

Scheduling with Thread Priorities


TheThreadclasscontainsanumberofmethodsandvariablesrelatedtothreadpriorities:

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.

Other Thread-Scheduling Methods


OthermethodsintheThreadclassalsoaffectscheduling.Forthemostpart,wedonotrecommendthatyouusethesemethods.Thesuspend()and
resume()methodsdirectlyaffectschedulingathreadthatissuspendedisintheblockedstate.However,aswediscussedinChapter2,thesemethodsare
deprecated.
TheThreadclassalsoincludesayield()method,whichasksthehostoperatingsystemtoselectanotherthreadtorun.Itseffectisverydependentonthe
operatingsystemhostingthevirtualmachinemuchofthetime,theyield()methodturnsouttobeanoop.Onthegreenthreadmodel(seethenext
section),theyield()methodcanbeveryuseful,butastheJavaplatformhasevolvedtosupportnativethreadsofanoperatingsystem,theyield()
methodhaslostitsvalue.

Popular Threading Implementations


We'llnowlookathowallofthisplaysoutintheimplementationoftheJavavirtualmachineonseveralpopularplatforms.Inmanyways,thisisasectionthat

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.

USER ANDSYST EM LEVELT HREADS


Inmostoperatingsystems,theoperatingsystemislogicallydividedintotwopieces:userandsystemlevel.Theoperatingsystemitselfthatis,the
operatingsystemkernelliesatthesystemlevel.Thekernelisresponsibleforhandlingsystemcallsonbehalfofprogramsrunattheuserlevel.
Whenaprogramrunningatuserlevelwantstoreadafileforexample,itmustcall(ortrap)intotheoperatingsystemkernel,whichreadsthefileand
returnsthedatatotheprogram.Thisseparationhasmanyadvantages,nottheleastofwhichisthatitallowsforamorerobustsystem:ifaprogram
performsanillegaloperation,itcanbeterminatedwithoutaffectingotherprogramsorthekernelitself.Onlywhenthekernelexecutesanillegal
operationdoestheentiremachinecrash.
Becauseofthisseparation,itispossibletohavesupportforthreadsattheuserlevel,thesystemlevel,oratbothlevelsindependently.

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.

Windows Native Threads


Inthenativethreadingmodelusedon32bitWindowsoperatingsystems,theOSisfullycognizantofthemultiplethreadsthatthevirtualmachineuses,and
thereisaonetoonemappingbetweenJavathreadsandoperatingsystemthreads.Therefore,theschedulingofJavathreadsissubjecttotheunderlying
schedulingofthreadsbytheoperatingsystem.
Thismodelisusuallysimpletounderstandbecauseeverythreadcanbethoughtofasaprocess.TheOSschedulermakesnorealdistinctioninthiscase
betweenaprocessandathread:ittreatseachthreadlikeaprocess.Ofcourse,therearestillotherdifferencesintheOSbetweenathreadandaprocess,butnot
asfarastheschedulerisconcerned.
Windowsoperatingsystemsuseacomplexprioritycalculationtodeterminewhichthreadshouldbethecurrentlyrunningthread.Thatcalculationtakesinto
accounttheWindowsthreadpriority.ThisisverysimilartotheJavalevelthreadprioritybetween0and10,exceptthatWindowsprovidesonly7priorities.
Therefore,someoverlapoccursasJava's11logicalprioritiesaremappedtoWindows7actualpriorities.Differentimplementationsofthevirtualmachinedo
thisdifferently,butonecommonimplementationperformsthemappinglistedinTable91.

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).

Solaris Native Threads


RecentversionsoftheSolarisOperatingEnvironmenthavehadtwodifferentthreadingmodels.Solaris7featuredacomplex,twolevelthreadingsystem,
withuserlevelthreadsandsystemlevellightweightprocesses(LWPs).JavathreadswereequivalenttoSolarisuserlevelthreads,andthereisanMtoN
mappingbetweentheuserlevelthreadsandLWPs.MuchoftheflexibilityofthismodelislostontheJavadeveloper,whocandirectlyinfluenceonlythe
priority(andnumber)oftheuserlevelthreadsbutnottheunderlyingLWPs.
InSolaris9,anewonetoonethreadingmodelisused.ThatmakesitconceptuallysimilartothemodelsonWindowsoperatingsystems,thoughits
implementationdetailsarequitedifferent.
InSolaris8,bothmodelsareavailable,andtheuserpicksamodelwhentheJavaprogram(oranyotherprogram)isexecuted.
ForJavaprograms,theonetoonemodelishighlypreferable,particularlywhenthemachinehasmultipleCPUsandtheJavathreadsareCPUintensive.In
othercases,theonetoonethreadingmodelisstillpreferred,thoughthedifferenceinthreadingmodelsisnotassignificant.Forthisreason,manyJava
programsrunbetteronSolaris9thanonSolaris7.OnSolaris8,youspecifythenewthreadingmodelbysettingtheenvironmentvariable
LD_LIBRARY_PATH=/usr/lib/lwpintheshell(orscript)inwhichtheJavaexecutableisstarted.
OnSolaris7,youcanmimicsomeofthebenefitsofSolaris'newthreadingmodelbyincludingthesetwoflagsinyourJavacommandline:
XboundthreadsXX:+UseLWPSynchronization.
ThecomplexpriorityofaSolaristhreadisdeterminedbythefollowing:
Solarisnativethreadsaresubjecttopriorityinheritance.
Theactualpriorityofathreadisavaluefrom0to59.Thatvalueisprimarilydeterminedbyhowlongit'sbeensincethethreadhasrun.Thoughthe
calculationincludestheJavalevelpriority,otherfactorsdominatethecalculation.
Solarisalsoincludesavarietyofschedulingclasses.Allthreadsinasingleprogrambelongtothesameschedulingclass,sothereisnovariabilityin
schedulingamongthem.
RunningourprioritybasedcalculatoronSolarisproducesthissortofoutput:

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.

Linux Native Threads


UntilJDK1.3,Linuxbasedvirtualmachinestendedtouseagreenthreadmodel.SomeusedLinux'snativethreads,butthekernelsupportforthosethreads
didnotsupportalargenumberofconcurrentthreads.JDK1.3addedsupportforLinuxnativethreads.However,theLinuxkernelatthetimewasnotoptimal
forthreadedapplicationsinparticular,thepscommandlistedallthreadsasiftheyweredifferentprocesses.
NewLinuxkernelsusetheNativePosixThreadLibrary(NPTL),whichprovidesthesameonetoonemappingofJavathreadstokernelthreadsthatwe've
seeninotheroperatingsystems.ThecomplexprioritycalculationforthosethreadsissimilartowhatwesawonSolaris,wheretheJavapriorityisonlya
smallfactorinthecalculation.JDK1.4.2isthefirstversionofJavatosupportthisnewkernel.

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.

Why Thread Pools?


Theideabehindathreadpoolistosetupanumberofthreadsthatsitidle,waitingforworkthattheycanperform.Asyourprogramhastaskstoexecute,it
encapsulatesthosetasksintosomeobject(typicallyaRunnableobject)andinformsthethreadpoolthatthereisanewtask.Oneoftheidlethreadsinthe
pooltakesthetaskandexecutesitwhenitfinishesthetask,itgoesbackandwaitsforanothertask.
Threadpoolshaveamaximumnumberofthreadsavailabletorunthesetasks.Consequently,whenyouaddatasktoathreadpool,itmighthavetowaitfor
anavailablethreadtorunit.Thatmaynotsoundencouraging,butit'satthecoreofwhyyouwoulduseathreadpool.
Reasonsforusingthreadpoolsfallintothreecategories.
Thefirstreasonthreadpoolsareoftenrecommendedisbecauseit'sfeltthattheoverheadofcreatingathreadisveryhighbyusingapool,wecangainsome
performancewhenthethreadsarereused.Thedegreetowhichthisistruedependsalotonyourprogramanditsrequirements.Itistruethatcreatingathread
cantakeasmuchasafewhundredmicroseconds,whichisasignificantamountoftimeforsomeprograms(butnotothersseeChapter14).
Thesecondreasonforusingathreadpoolisveryimportant:itallowsforbetterprogramdesign.Ifyourprogramhasalotoftaskstoexecute,youcan
performallthethreadmanagementforthosetasksyourself,but,aswe'vestartedtoseeinourexamples,thiscanquicklybecometediousthecodetostarta
threadandmanageitslifecycleisn'tveryinteresting.Athreadpoolallowsyoutodelegateallthethreadmanagementtothepoolitself,lettingyoufocusonthe
logicofyourprogram.Withathreadpool,yousimplycreateataskandsendthetasktothepooltobeexecutedthisleadstomuchmoreelegantprograms
(seeChapter11).
Theprimaryreasontouseathreadpoolisthattheycarryimportantperformancebenefitsforapplicationsthatwanttorunmanythreadssimultaneously.In
fact,anytimeyouhavemoreactivethreadsthanCPUs,athreadpoolcanplayacrucialroleinmakingyourprogramseemtorunfasterandmoreefficiently.
Ifyoureadthatlastsentencecarefully,inthebackofyourmindyou'reprobablythinkingthatwe'rebeingawfullyweasely:whatdoesitmeanthatyour
program"seems"torunfaster?WhatwemeanisthatthethroughputofyourCPUboundprogramrunningmultiplecalculationswillbefaster,andthatleads
totheperceptionthatyourprogramisrunningfaster.It'sallamatterofthroughput.

Thread Pools and Throughput


InChapter9,weshowedanexampleofwhathappenswhenasystemhasmorethreadsthanCPUresources.Thewayinwhichthethreadsperformthe
calculationhasabigeffectontheoutput.Inparticular,ourfirstexampleproducesthisoutput:

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.

Why Not Thread Pools?


Ifyourprogramisdoingbatchprocessing,orsimplyprovidingasingleanswerorreport,itdoesn'treallymatterifyouuseasmanythreadsaspossibleora
threadpool:ifnooneisinterestedintheresultsgivenbyeachthread,itdoesn'tmatterifsomeofthemfinishbeforeothers.Thatdoesn'tmeanthatyoucan
expecttocreatethousandsofthreadswithimpunity:threadstakememory,andthemorememoryyouuse,themoreimpactyou'llhaveonyoursystem
performance.Additionally,thereissomeslightoverheadwhentheoperatingsystemmanagesthousandsofthreadsinsteadofjustafew.Still,ifyour
programdesignnicelyseparatesintomultiplethreadsandyou'reinterestedonlyintheendresultofallthosethreads,athreadpoolisn'tnecessary.
ThreadpoolsarealsonotnecessarywhenavailableCPUresourcesareadequatetohandlealltheworktheprogramneedstodo.Infact,inthiscaseathread
poolmaydomoreharmthangood.Obviously,ifyoursystemhaseightCPUsandyouhaveonlyfourthreadsinyourthreadpool,taskswaitforathread
eventhoughfourCPUsareidle.Withathreadpool,youwanttothrottlethetotalnumberofthreadssothattheydon'toverwhelmyoursystem,butyounever
wanttohavefewerrunnablethreadsthanCPUs.

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.

Using a Thread Pool


Touseathreadpool,youmustdotwothings:youmustcreatethetasksthatthepoolistorun,andyoumustcreatethepoolitself.Thetasksaresimply
Runnableobjects,sothatmesheswellwithastandardapproachtothreading(infact,thetaskthatwe'lluseforthisexampleisthesameRunnabletaskwe
useinChapter9tocalculateaFibonaccinumber).YoucanalsouseCallableobjectstorepresentyourtasks(whichwe'lldolaterinthischapter),butfor
mostsimpleuses,aRunnableobjectiseasiertoworkwith.
ThepoolisaninstanceoftheThreadPoolExecutorclass.ThatclassimplementstheExecutorServiceinterface,whichtellsushowtofeedittasks
andhowtoshutitdown.We'lllookattheotheraspectsofthatclassinthissection,beginningwithhowtoconstructit.

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.

Queues and Sizes


Thetwofundamentalthingsthataffectathreadpoolareitssizeandthequeueusedforthetasks.Thesearesetintheconstructorofthethreadpoolthesize
canchangedynamicallywhilethequeuemustremainfixed.Inadditiontotheconstructor,thesemethodsinteractwiththepool'ssizeandqueue:
packagejava.util.concurrent;
publicclassThreadPoolExecutorimplementsExecutionService{
publicbooleanprestartCoreThread();
publicintprestartAllCoreThreads();
publicvoidsetMaximumPoolSize(intmaximumPoolSize);
publicintgetMaximumPoolSize();
publicvoidsetCorePoolSize(intcorePoolSize);
publicintgetCorePoolSize();
publicintgetPoolSize();
publicintgetLargestPoolSize();

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.

Callable Tasks and Future Results


Executorsingeneraloperateontasks,whichareobjectsthatimplementtheRunnableinterface.Inordertoprovidemorecontrolovertasks,Javaalso
definesaspecialrunnableobjectknownasacallabletask:
packagejava.util.concurrent;
publicinterfaceCallable<V>{
public<V>call()throwsException;
}

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.

The FutureTask Class


YoucanassociateaRunnableobjectwithafutureresultusingtheFutureTaskclass:

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.

Overview of Task Scheduling


Interestingly,thisisnotthefirsttimethatwehavebeenconcernedwithwhenataskistobeexecuted.Previously,we'vejustconsideredthetimingaspartof
thetask.We'veseentoolsthatallowthreadstowaitforspecificperiodsoftime.Hereisaquickreview:

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.

The java.util.Timer Class


Thejava.util.TimerclasswasaddedtoJDK1.3specificallytoprovideaconvenientwayfortaskstobeexecutedasynchronously.Thisclassallowsan
object(ofaspecificclasswe'lllookat)tobeexecutedatalatertime.Thetimecanbespecifiedeitherrelativetothecurrenttimeorasanabsolutetime.This
classalsosupportstherepeatedexecutionofthetask.
TheTimerclassexecutestaskswithaspecificinterface:
publicabstractclassTimerTaskimplementsRunnable{
protectedTimerTask();
publicabstractvoidrun();
publicbooleancancel();
publiclongscheduledExecutionTime();
}

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.

Using the Timer


Here'sanexamplethatusestheTimerclass.Theexampleprogramallowsyoutomonitorthereachabilityofoneormorewebsites:itperiodicallyattempts
toretrieveaURLfromeachwebsite.Websitesthatarereachablearedisplayedingreenwebsitesthataredownaredisplayedinred.
Westartwiththetimertaskthatcontactsthewebsite:
packagejavathreads.examples.ch11.example1;

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.

The javax.swing.Timer Class


Aswe'vediscussed,SwingobjectscannotbeaccessedfromarbitrarythreadswhichincludesthethreadsfromtheTimerclass(andthethreadsinthe
threadpooloftheScheduledThreadPoolExecutorclassthatwediscusslaterinthischapter).WeknowthatwecanusetheinvokeLater()and
invokeAndWait()methodsoftheSwingUtilitiesclasstoovercomethis,butJavaalsoprovidesaTimerclassjustforSwingobjects.The
javax.swing.Timerclassprovidestheabilitytoexecuteactionsataparticulartime,andthoseactionsareinvokedontheeventdispatchingthread.
Hereistheinterfacetothejavax.swing.Timerclass:

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.

Using the javax.swing.Timer Class


Wecanusethejavax.swing.Timerclassinourtypingprogram.Previously,ouranimatedcanvassetupathreadtohandletheanimationthisthread
periodicallytoldtheanimationcanvastorepaintitself.Now,we'lluseatimer.
packagejavathreads.examples.ch11.example2;

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.

The ScheduledThreadPoolExecutor Class


J2SE5.0introducedtheScheduledThreadPoolExecutorclass,whichsolvesmanyproblemsoftheTimerclass.Inmanyregards,theTimerclass
canbeconsideredobsoletebecauseoftheScheduledThreadPoolExecutorclass.Whyisthisclassneeded?Let'sexaminesomeoftheproblemswith
theTimerclass.
First,theTimerclassstartsonlyonethread.Whileitismoreefficientthancreatingathreadpertask,itisnotanoptimalsolution.Theoptimalsolutionmay
betouseanumberofthreadsbetweenonethreadforalltasksandonethreadpertask.Ineffect,thebestsolutionistoplacethetasksinapoolofthreads.The
numberofthreadsinthepoolshouldbeassignableduringconstructiontoallowtheprogramtodeterminetheoptimalnumberofthreadsinthepool.

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.

Using the ScheduledThreadPoolExecutor Class


Here'samodificationofourURLmonitorthatusesascheduledexecutor.Modificationofthetaskitselfmeansasimplechangetotheinterfaceitimplements:
packagejavathreads.examples.ch11.example3;
...
publicclassURLPingTaskimplementsRunnable{
...
}

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.

Using the Future Interface


Theothercasewhenusingascheduledexecutormakessenseiswhenyouwanttousethecallableinterfacesothatyoucanlatercheckthestatusofthetask.
Thisislogicalequivalenttousingthejoin()methodtotellifathreadisdone.
We'llextendourexampleslightlytoseehowthisworks.Let'ssupposewewantourURLmonitortohavealicensewithoutalicense,itrunsinademo
modefortwominutes.Intheabsenceofavalidlicense,wecansetupacallabletaskthatrunsafteradelayoftwominutes.Afterthattaskhasrun,weknow
thatthelicenseperiodhasexpired.
We'llhavetopollthelicensetaskperiodicallytoseewhetherithasfinished.Normally,wedon'tlikepollingbecauseofitsinefficiencies,butinthiscase,we
haveaperfecttimetodoit:becausethestatusthreadrunseveryfiveseconds,itcanpollthelicensetaskwithoutwastingmuchCPUtimeatall.Sinceinthis
casewedon'thavetounnecessarilywakeupapollingthread,wecanaffordthesimplemethodcalltohandlethepoll.
First,weneedasimpletask.
packagejavathreads.examples.ch11.example4;

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.

Chapter12.Threads and I/O


Ifyou'renotinterestedinparallelprocessing,theareawhereyou'remostlikelytoencounterthreadsinJavaisindealingwithI/Oandparticularlyindealing
withnetworkI/O.That'sthetopicweexploreinthischapter.
InearlyversionsofJava,allI/Owasblocking.Ifyourprogramattemptedtoreaddatafromasocketandnodatawaspresent,theread()methodwould
blockuntilatleastsomedatawasavailable.Thatsituationisalsotrueofreadingafile.Forthemostpart,delaysinreadingfilesaren'tnoticeableyoumay
havetowaitafewcyclesforthedisktorotatetothecorrectlocationandtheoperatingsystemtotransferdatafromthedisk.Inmostprograms,blockingfor
thatamountoftimemakeslittledifference,butinthoseprogramswhereitdoesmakeadifference,theconceptsthatapplytonetworkI/Oarejustasrelevant
tofileI/O.
FornetworkI/O,thedelaycanbequitesignificant.Networksaresubjecttodelaysatvariouspoints(particularlyifthenetworkinvolveslongdistancesor
slowlinks).Evenifthere'snophysicaldelayonthenetworklines,networkI/Oisdoneinthecontextofaconversationbetweentwopeers,andapeermay
notbereadytofurnishitsoutputwhenitspartnerwantsit.Adatabaseserverreadscommandsfromauser,buttheusermaytakeafewminutestotypeinthe
SQLtobeexecuted.OncetheSQLhasbeensenttothedatabase,theuserisreadytoreadbacktheresponse,butitmaytakethedatabaseafewminutesto
obtaintheresultsofthequery.
BecauseearlyversionsofJavadidnothaveawaytohandlenonblockingI/O,Javaserverswouldtypicallystartanewthreadforeveryclientthatconnectedto
them.Javaclientswouldtypicallystartanewthreadtosendrequeststotheserversothattherestoftheprogramwouldremainactivewhiletheclientwas
waitingforaresponse.
InJDK1.4,thissituationchanged:JavaintroducedtheNIOpackage,whichalloweddeveloperstoutilizenonblockingI/Ointheirprograms.Thischanged
theruleforthewayinwhichJavaservers(andotherI/Ointensiveprograms)arethreaded,thoughitdoesnoteliminateallthreadingconsiderationsfrom
thoseprograms.
Inthischapter,welookatserversthatemployeachtypeofI/Oandshowcommontechniquesforhandlingtheserver'sthreads.

A Traditional I/O Server


Let'sstartwiththesimplestcase,whichisbasedonJava'soriginal(blocking)I/Omodel.Inthismodel,anetworkservermuststartanewthreadforevery
clientthatattachestotheserver.Wealreadyknowthatbyreadingdatafromasocketinaseparatethread,wesolvetheproblemofblockingwhilewe're
waitingfordata.Threadingontheserversidehasanadditionalbenefit:byhavingathreadassociatedwitheachclient,wenolongerneedtoworryaboutother
clientswithinanysinglethread.Thissimplifiesourserversideprogramming:wecancodeourclassesasifwewerehandlingasingleclientatatime.
Beforeweshowthecodefortheserver,let'sreviewsomenetworkingbasics.Figure121showsthedataconnectionsbetweenseveralclientsandaserver.
Theserversidesocketsetupisimplementedintwosteps.First,aninstanceoftheServerSocketclassisusedtolistenonaportknowntotheclient.The
clientconnectstothisportasameanstonegotiateaprivateconnectionwiththeserver.

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.

An Example Multithreaded Server

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.

USING THE MULTITHREADED SERVER


Nowwemustdeveloptheclientsideofourfirstexample.Weuseourstandardtypingprogramastheclientandchangeitsrandomcharactergeneratorto
connecttoourserverandsendcharactersretrievedfromthatserver.Here'stherandomcharactergeneratorthataccomplishesthat:
packagejavathreads.examples.ch12.example1;

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.

Scaling Using Traditional I/O


Theprimaryissuewhenusingtheserverwe'vejustimplementedisthatitcanhandleonlyafinitenumberofclients.Twofactorslimitthenumberofclients

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.

A New I/O Server


Whenyouneedtohandlealargenumberofclientsmakinganarbitrarynumberofrequests,theexampleswe'veseensofarareimpractical.Thetraditional
I/Oservercannotscaleuptothousandsofclients,andthetraditionalthrottledI/Oserverissuitableonlyforshortlivedrequests.
Becauseofthissituation,JavaintroducedanewI/Opackage(java.nio)inJDK1.4.TheI/OclassesinthispackageallowyoutousenonblockingI/O.
ThisobviatestheneedforasinglethreadforeveryI/Osocket(orfile)instead,youcanhaveasinglethreadthatprocessesallclientsockets.Thatthreadcan
checktoseewhichsocketshavedataavailable,processthatdata,andthencheckagainfordataonallsockets.Dependingontheoperationstheserverhasto
perform,itmayneed(orwant)tospawnsomeadditionalthreadstoassistwiththisprocessing,butthenewI/Oclassesallowyoutohandlethousandsof
clientsinasinglethread.
Giventhisefficiency,whywouldyoueverusethetraditionalI/Opatternswelookedatearlier?Asyou'llsee,theanswerliesinthecomplexityofthecode.
DealingwithnonblockingI/OismuchharderthandealingwithblockingI/O.Inthosesituationswhereyouhaveaknownsmallnumberofclients,theease
ofdevelopmentwiththetraditionalI/Oclassesmakesthejobofdevelopingandmaintainingyourcodemuchsimpler.Inothercases,however,theruntime
efficienciesofthenewI/Oclassesmakeupforitsinitialprogrammingcomplexity.

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.

A Single-Threaded NIO Server


Nowwe'lldevelopourthirdexample:asinglethreadednetworkserverthatusestheNIOclasses.Asbefore,we'lldevelopagenericNIOserverandthe
exampleserverthatcompletestheimplementationoftheserver.Wedonotneedtodevelopanewclienttousethisserver,however.TheTCPprotocolisthe
samewhetherweusetraditionalI/OorNIO.Asaresult,wecanusetheclientfromexample1toconnecttothisserver.
We'llstartwiththegenericNIOserver:
packagejavathreads.examples.ch12;

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.

A Multithreaded New I/O Server


OurnewI/Oserverisveryefficientathandlingalargenumberofclients,butitmaynotbemakingthebestuseofmachineresources.Ifourserverhas
multipleCPUs,weuseonlyoneofthem.Inothercases,wemighthaveahandleClient()methodthatmakesadatabasecall,inwhichcasethe
handleClient()methoditselfmayneedtowaitforaresponse(wecouldofcourseusenonblockingI/Otohandlethedatabasecall,butthatwouldmake
ourprogrammingevenmoredifficult).SooccasionallyyouwanttousenonblockingI/Otohandlealargenumberofclientsbutstillmultithreadyour
programforeaseofdevelopmentandoptimaluseofmachineresources.
Thissituationishandledwithathreadpool:asrequestscomeintotheserver,thehandleClient()methodplacestherequestsonthethreadpoolqueue.
Threadsinthepooltaketherequestsinorderandexecutetheminparallel.
Forourfourthexample,weadaptourcodefromChapter10andturnitintoaserverthatcansatisfyalargenumberofclientrequests.
packagejavathreads.examples.ch12.example4;

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.

Chapter13.Miscellaneous Thread Topics


ThreadsareabasicfeatureoftheJavaplatform.Asaresult,threadsinteractwithseveralofJava'sotherprogrammingandruntimefeatures.Inthischapter,
we'llbrieflytouchonsomeofthesefeatures(andissues),includingthreadgroups,Javasecurity,daemonthreads,classloading,exceptionhandling,and
memoryusage.Someofthesetopicsareinterrelated:inparticular,thethreadgroupclassisusedbythesecuritymanagerasthebasisforitsdecisions.In
general,thetopicsherewillcompleteyourunderstandingofhowthreadspermeatetheJavaplatform.

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.

Threads and Java Security


OneofJava'shallmarksisthatitisdesignedfromthegroundupwithsecurityinmind.It'snosurprise,then,thatthreadshaveanumberofinteresting
securityrelatedproperties.
Initsdefaultconfiguration,securityinaJavaprogramisenforcedbythesecuritymanager,aninstanceofthejava.lang.SecurityManagerclass.
Whencertainoperationsareattemptedonthreadsorthreadgroups,theThreadandThreadGroupclassesconsultthesecuritymanagertodetermineif
thoseoperationsarepermitted.
ThereisonemethodintheSecurityManagerclassthathandlessecuritypoliciesfortheThreadclassandonethathandlessecuritypoliciesforthe
ThreadGroupclass.Thesemethodshavethesamenamebutdifferentsignatures:
voidcheckAccess(Threadt)
Checksifthecurrentthreadisallowedtomodifythestateofthethreadt

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.

SECURIT YANDT HECHECKACCESS( ) MET HO D


BoththeThreadandThreadGroupclasseshaveaninternalmethodcalledcheckAccess()thismethod,bydefault,callsthesecuritymanager's
checkAccess()method,passingtheappropriatethreadorthreadgroupobject.
ThecheckAccess()methodwithintheThreadandThreadGroupclassesispublic,soyoucancallitdirectlyfromanythreadorthreadgroupobjectif
youwanttocheckwhatsecuritypolicyisinplace.
ThecheckAccess()methodwithintheThreadGroupclassisfinalitmaynotbeoverridden.ThecheckAccess()methodoftheThreadclass,
however,isnotfinal,meaningthatyoucouldoverrideitandeffectivelychangethesecuritymodelforyourparticularThreadsubclass.Remember,
however,thatthiswouldaffectonlyyourclassandnototherthreadswithinthesystem.

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.

Threads and Class Loading


ClassesinJavaareloadedbyaclassloaderobject,whichconsultsthedirectoriesandjarfilesinyourclasspathtofindtheclassdefinitions.Applicationscan
constructtheirownclassloaderstofindclassfilesinlocationsotherthantheclasspath.Forexample,theJavaPluginconstructsaclassloaderforeachapplet
basedonthecodebasespecifiedintheapplet'stagJ2EEapplicationserversconstructaclassloaderforeachJ2EEapplicationtheyrun.
Classloadersformahierarchy.Therootofthehierarchyisthebootstrapclassloader,whichloadsclassesfromrt.jarandothersystemjarfiles.Itsimmediate
childistheapplicationclassloaderwhichloadsclassesfromtheclasspath.Fromthatpoint,theclassloadingtreecanbecomearbitrarilycomplicated.Figure
132showstheclassloadinghierarchyofaprogramthathasstartedtwodifferentclassloaders.Notethattwoclassesinthishierarchyhavethesamename:
it'saninterestingpropertyofthevirtualmachinethatclassesloadedbydifferentclassloadersareconsideredcompletelydifferentclasses.

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).

Threads and Exception Handling


InChapter2,weexaminehowtocreateathreadandwementionthatthestart()methodperformssomeinternalhousekeepingandthencallstherun()

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.

The ThreadDeath Class


TheThreadDeathclassisaspecialThrowableclassthatwasformerlyusedtostopathread.Whenthestop()methodiscalledonathread,thatthread
immediatelythrowsaThreadDeatherror.TheThreadDeathclassextendstheErrorclassandsoisnotusuallycaughtbyapplicationprogramming.It
ispossible,ofcourse,tocatchanyThrowableobject,butitisnotadvisabletousethistechniquetopreventthedeathofthethread.Afterall,ifwedidn't
wantthethreadtodie,whywasthestop()methodcalled?Andifwepreventthethreadfromexiting,anotherthreadexecutingthejoin()methodnever
completes.
TheThreadDeathclassiswhatcausedthestop()methodtobecomedeprecated.Becauseit'sthrownimmediatelyuponreceiptofthestop()method,it
hasthepotentialtoleaveshareddatainaninconsistentstate.Andbecauseitreleasesanylocksonsynchronizedblocksordatathatitholds,ithasthepotential
toallowotherthreadstoaccesstheinconsistentdata,evenifthatdataiscorrectlysynchronized.
TheuncaughtException()methodhandlestheThreadDeathclassdifferently:whileitprintsoutastacktraceforallothererrorsandexceptions,the
threaddeatherrorsaresilentlyswallowed.
ThisleadsustoonelimitedcircumstanceinwhichtheThreadDeathclassisusefulasareplacementforthestop()method.Supposethatathread
encountersanerrorandwantstoterminateitself,buttheerrorisnotegregiousenoughthatitwantstheusertoseetheerror.Thenormalwaytodothisisto
returnfromtherun()method,butitmaybedifficultforthethreadtounwindallofitsmethodsinordertodothat.Asecondwayisforthethreadtocallthe
stop()methodonitself.ThethirdandfinalwayisforthethreadtothrowaThreadDeatherror.
Evenso,athreadthatwantstoterminateitselfcannotsimplythrowaThreadDeatherrorwillynilly:thethreadmustthrowthisobjectonlywhenitissure
thatithasnotleftanydatainapossiblyinconsistentstate.Ifyou'veprogrammedyourthreadverycarefullyandaresurethatthethreadhasleftalldataina
consistentstate,it'ssafetothrowtheThreadDeathobjecttomakeyourthreadexitimmediately.Theonlydifferencebetweenthisandthethreadcallingthe
stop()methodonitselfisthatthecompilerwarnsyouaboutthedeprecatedmethodinthelattercase(evenifathreadknowsit'ssafetocallstop()on
itself).ThecompilerdoesnotcomplainifyouthrowaThreadDeathobject.Still,youhavetobeverycarefulonlytodothiswhenit'sabsolutelysafetodo
so.

Threads, Stacks, and Memory Usage


InChapter2,wementionthatwhenyouconstructathread,youcanspecifyitsstacksize.UsingthisparticularconstructorcanleadtounportableJava
programsbecausethestackdetailsofthreadsvaryfromplatformtoplatform.We'llexplainthedetailsinthissection.
Thestackiswhereathreadkeepstrackofinformationaboutthemethodsit'scurrentlyexecuting.Let'slookagainatourclassthatcalculatesFibonacci
numbers:

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.

Stack Overflow Errors


Javastackshaveafixedsize,say1024KB.Supposethatthesizeofastackframeforthefib()methodis16bytes:4bytesforthevariablen,and12bytes
forotherinformation.Ifweattempttocalculatefib(65536),we'llgetastackoverflowerror:allthestackframesneededfortherecursivecallstothe
fib()methodwon'tfitintheavailablememory.
Thisisonecasewhereyoumightwanttousethestacksizeargumenttotheconstructorofthethreadobjecttoallocatemorespaceforthethread.Ifyou
specifyastacksizelargerthan1024KB,you'llbeabletocalculatealargerFibonaccinumberwithoutgettingastackoverflowerror.Notethattheconstructor
thatallowsyoutospecifythethread'sstacksizeisavailableonlybeginningwithJ2SE5.0andthatthenumberspecifiedisonlyahinttothevirtualmachine.
TheoperatingsystempicksasizeforthestackthatmeetsanyOSlevelrequirements(e.g.,stacksizesoftenmustbeamultipleof128KB).
Theproblemhereiswhatvaluetopickforthestacksize.Theoptimalvalueisdependentontwothings:themaximumnumberofframesyou'llneedtostore
andthesizeofeachframe.Youmaybeabletofigureoutthefirstvalueinadvance,butthesecondvalueisdifferentondifferentplatformsandwithdifferent
Javaimplementations.Allowingtheusertospecifythestacksizeforthethreadthroughacommandlineargument(orsomeothermechanism)isagood
compromisesincetheusercouldchangethatnumberbasedonherparticularcircumstances.

Out of Memory Errors


Inanumberofcases,JavaprogramsmaythrowanOutOfMemoryError.Thecommoncase,ofcourse,iswhenyouattempttocreateanewobjectandthe
Javaheaphasnoroomtostorethatobject.
Itisalsopossibletogetanoutofmemoryerrorwhenyouconstructathread.Inthatcase,theerrormightstillbecausedbecauseyourheapistoosmallto
storethethreadobject.Butit'sfarmorelikelythatthesystemcannotallocatespaceforthethread'sstack.
JavastacksarenotstoredintheJavaheaptheyarestoredinthegeneralmemoryofthevirtualmachine.Thiseffectivelylimitsthenumberofthreadsthatan
applicationcancreate.
SupposeagainthatadefaultJavastackis1024KB.OnaLinuxorWindowsplatform,themaximummemorysizeofaprogramis2GB,butthatsize
includestheJavaheapandallthesharedlibraries(DLLs)thatthevirtualmachineuses.Ifwespecifyamaxheapsizeof768KBandassumethattheshared
librariesandexecutablecodetakeup256KB,thatleavesus1GBinwhichtostorethreadstacks.Inthisscenario,we'llbeabletocreateabout1024threads
witha1024KBstacksizebeforewegetanoutofmemoryerror.
OnSPARCsystemsrunningSolaris,themaximumsizeofaprocessis4GB,leavingusthatmuchmorememoryavailableforstacks(and/orabiggerheap).
MostSPARCsystems,andsomeAMDandIntelsystems,canruna64bitvirtualmachine,effectivelymakingthisissueirrelevant.
Sodependingonthenumberofthreadsyouneedtocreateandtheplatformyou'rerunningon,youmightneedtospecifyasmallerstacksizeforatleastsome
ofthethreadsinordertofitintheavailablememory.

Specifying Stack Sizes


We'vealreadymentionedthatthestacksizeforathreadcanbespecifiedwhenthethreadisconstructed.Thatargument,availableonlyinJ2SE5.0,allows
youtohaveadifferentstacksizefordifferentthreads.
OnmanyJavaimplementations,youcanalsospecifythestacksizeforallthreadsusingacommandlineargumenttypically,theXssargument.For

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

Measuring Java Performance


MeasuringperformanceofaJavaprogrampresentscertaindifficulties,particularlywhenattemptingtomeasureisolatedtasks(aswedointhischapter).If
you'reinterestedinhowquicklyaprogramcanperformafixedsetoftasks,measurementiseasysincetheelapsedtimetoruntheprogramisanadequate
answer.
Ifyou'reinterestedinmeasuringtheperformanceofalongrunningJavaprogram(andinparticular,aJavaserverapplication),therearecertainthingsyou
mustaccountfor.JavavirtualmachinesperformjustintimecompilationoftheJavabytecodesthatmakeupanapplication.Thismeansthatthelongeran
applicationruns,themoreefficientthecodebecomes:moreofitbecomescompiled,moremethodsbecomeinlined,moreloopsbecomeunrolled,andsoon.
Measuringperformanceinthissituationrequiresarampuptimethatallowsthecompilerachancetoperformasignificantpartofitsoptimizations.Intruth,
theoptimizationsperformedbythecompilerarenevercompletelyoverperformanceofaJavaprogramcontinuallyimproves.However,thebulkofthe
optimizationsareperformedduringthefirstfewminutesoftheprogram,sowaitingjustashorttimebeforetimingperformanceisusuallysufficient.
Ifyou'rewritingabenchmarktotestperformanceofaparticularoperation,youmusttakethisintoaccount.Yoursyntheticbenchmarkshouldrunthroughthe

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.

Atomic Variables and Contended Synchronization


Next,let'slookatthedifferencebetweenusingclassesinthejava.util.concurrent.atomicpackageversusregularsynchronization.Wementionedin
Chapter5thatusinganatomicvariableisonewayinwhichsynchronizationcanbeavoided.We'llseethebenefitsofdoingthatinthissection.Unlikeourlast
test,we'lllookatcontendedlocks(sincewealreadyknowthatuncontendedlockssufferlittleperformancepenalty).
Atomicvariablesofferadvantagesotherthanperformance:theyneatlyencapsulateoperations,andtheypreventinadvertentaccesstodatafrom
unsynchronizedcode.Soquiteapartfromanyperformancebenefitthattheymayormaynotoffer,theiruseoffersimportantcontributionsfromadeveloper's
pointofview.
Forthistest,wegaugetheperformanceofincrementinganintegervariable.Inonecase,wewriteasynchronizedmethodthatincrementstheintegerinthe
secondcase,wecalltheAtomicInteger.getAndIncrement()method.Wetestaccesstothesemethodsfromasinglethreadandfrommultiplethreads
simultaneously,obtainingtheresultsinTable142.
Table142.Timedifferencebetweenusingatomicvariablesversussynchronizedmethods

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.

The ConcurrentHashMap Class


AninterestingcaseariseswiththeConcurrentHashMapclass,whichallowsthreadstoaccessahashtableconcurrentlyyetstillprovidesthreadsafety.
Comparingoperationsthatinsertandretrievevaluesfromahashtableandaconcurrenthashmap,weseethedifferencesinTable143.
Table143.TimedifferencebetweenConcurrentHashMapandHashtable

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).

Thread Creation and Thread Pools


Thefinalperformanceaspectwe'lldiscussisthreadcreationandtheuseofthreadpools.Acommonassumptionisthatcreatingathreadisanexpensive
operationandthatthisshouldbeavoidedbyusingathreadpoolwheneverpossible.Isthatactuallyagoodidea?
ToreiterateoneofthepointswemakeinChapter10:oneofthedeterminingfactorsinusingathreadpoolisthedesignofyourprogram.Ifthedesignof
yourprogrammoreeasilylendsitselftostartingnewthreads,youshoulddothatifitmoreeasilylendsitselftocreatingtasksandfeedingthemtoan
executor,youshoulddothat.Andtheperceivedperformanceofaprogramcanoftenbeimprovedbyusingathreadpooltothrottlethenumberofactive
threadsonamachine.
Thatsaid,isitreallymoreefficienttouseathreadpoolthantospawnanewthread?Theanswerisyes,butnotalwaystoanextentthatitaffectsyour
program.Toreachthisconclusion,wehavewrittenamethodthatincrementsthevalueofanatomicinteger.Werunthismethodthreedifferentways:ina
simpleloop,inaRunnableobjectfeedingtoathreadpool,andinaRunnableobjectbeingusedtocreateanewthread.Subtractingthetimerequiredto
executethemethodinaloopfromthetimerequiredtoexecutethemethodinathreadpool(oranewthread)allowsustoobtainthevaluesshowninTable
144.
Table144.Timedifferencebetweenthreadcreationandthreadpool

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.

Chapter15.Parallelizing Loops for Multiprocessor Machines


Inpreviouschapters,weexaminedthreadingasatechniquethatallowsustosimplifyprogramming:weusedthreadingtoachieveasynchronousbehavior
andperformindependenttasks.Althoughwediscussedhowthreadsarescheduledonmachineswithmultipleprocessors,byandlargethetechniquesthat
we'veshownsofararenotaffectedbyamachinewithmultipleprocessorsnordotheyexploitthenumberofprocessorsonamachinetomaketheprogram
runfaster.
Multithreadedprogramshaveaspecialbondwithmultiprocessorsystems.Theseparationofthreadsprovidesaclearandsimpleseparationforthe
multiprocessormachine.Sincetheoperatingsystemcanplacedifferentthreadsondifferentprocessors,theprogramrunsfaster.
Inthischapter,we'lllookathowtoparallelizeJavaprogramssothattheyrunfasteronamachinewithmultipleCPUs.Theprocessesthatweexamineare
beneficialnotonlytonewlydevelopedJavaprogramsbutalsotoexistingJavaprogramsthathaveaCPUintensiveloop,allowingustoimprovethe
performanceofthoseprogramsonamultiprocessorsystem.
HowdoestheJavathreadingsystembehaveinamultiprocessorsystem?Therearenoconceptualdifferencesbetweenaprogramrunningonamachinewith
oneprocessorandamachinewithtwoormoreprocessorsthreadsbehaveexactlythesameineithercase.Therealdifferenceisthatthethreadsactuallydo
executesimultaneously.InChapter2,wediscussedhowtheoperatingsystemswitchesbetweenthelistofinstructionsforcertainthreadsandhowthat
switchinggavetheillusionofsimultaneity.Onamultiprocessorsystem,thesimultaneityisreal.
ForJavadevelopers,threadedcoderunningonmultipleprocessorsmeansthatraceconditionsthathappenveryinfrequentlyonasingleprocessorsystemare
muchmorelikelytooccur.Hopefully,youhavebynowlearnedtowritethreadsafeprograms.Testingthoseprogramsonamultiprocessormachineisone
goodwaytobemoreconfidentintheresults.

Parallelizing a Single-Threaded Program


Withoutredesigningaprogram,thebestareatoparallelizethatis,theareainwhichtointroducemultiplethreadstoincreasetheprogram'sperformanceis
wheretheprogramisCPUbound.Afterall,itdoesn'tmakesensetobringinmoreprocessorsifthefirstprocessorcannotstaybusy.Inmanyofthecases
wheretheprocessisCPUboundthatis,theprocessisusingallofthecomputerprocessors'cycleswhilenotusingthedisksorthenetworkatfullcapacity
theprogram'sspeedincreaseswiththeadditionofmoreprocessors.Theprocesscouldbeinvolvedinalongmathematicalcalculationor,morelikely,in
largeiterationsofshortermathematicalcalculations.Furthermore,thesecalculationsprobablyinvolvealargecontrollooporevenalargenumberofloops
insideloops.Thesearethetypesofcommonalgorithmsthatweexaminehere.Considerthefollowingcalculation:

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.

AUTO PARALLELIZ ING CO MPILERS


TheterminologyusedinthischapterisbasedontheterminologyusedbytheautoparallelizingMPCcompilerfortheSolarisOperatingEnvironment.
Automaticparallelizationisthesametechniquethatwearedescribinginthischapter,butitisaccomplishedbythecompilerinsteadofbythe
programmer.AutoparallelizationiseasiertoaccomplishinalanguagesuchasFORTRANthanC.ThisisduetothealiasingproblemswiththeC
language:withpointersandotheraliasingissues,itisverydifficulttoclassifythevariablesortheloopitself.Evenwiththecurrentimplementation,
#pragmasareneededtohelpthecompilerclassifyvariablesusedintheloop.
Inthisregard,JavaisclosertoFORTRANthantoC.Allvariablereferencesaretracked(forgarbagecollection),pointerarithmeticisnotallowed,and
variabletypesareenforced.JavahasfeweraliasingproblemsthanC.Thismeansthatitshouldbemucheasiertodevelopanautoparallelizing
compilerforJavathanitisforC.Untiloneexists,however,youneedtoapplythesetechniquesbyhand,aswe'vedoneinthischapter.

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.

Loop Scheduling and Load Balancing


Wedefinetheprocessofdistributingtheiterationsofthelooptotheindividualthreadsasloopscheduling.InourLoopHandlerclass,thisishandledbythe
loopGetRange()method.Tomaximizeprocessorusage,weshoulddistributetheworktothethreadsasevenlyaspossible,withtheleastamountof
overheadindeterminingthisdistribution.Thisisdefinedasloadbalancing.
Thebasicloopschedulingtypesatourdisposalincludestaticorchunkscheduling,selfscheduling,guidedselfscheduling,anduserdefinedscheduling.

STATIC OR CHUNK SCHEDULING


Understaticscheduling,eachthreadisassignedanequalnumberofiterationsthatdependsonthenumberofthreadsavailable.If1000loopiterationsareto
bedistributedand10threadsareassignedtothetask,eachthreadisassigned100iterationsoftheloop.Thisisthealgorithmthatisusedbythe
LoopHandlerclass.Thealgorithmalsoadds1tothesizetomakesurethatthedistributionisroundedup.Otherwise,theremightbeaniterationleftover
andaworkerthreadwouldhavetoperformthatsingleiterationafteralreadyperformingtheoriginalchunk.
Theproblemwiththisalgorithmisthatitassumesthateachiterationofthelooptakesthesameamountoftime.Ifthisisnottrue,oneofthethreadstakes
moretimethantheotherthreadstocomplete.Sincealltheworkisdividedupatthebeginningoftheloop,theotherthreadsareidlewhilethefinaliterations
arecompletedbythelastremainingthread.

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.

SO MET IMESO RDERDO ESMAT T ER


Intheexamplesofthissection,weassumethatwecanperformtheadditioninanyorderthatwelike.Sinceadditionisassociative,thisissupposedto
work.
Onacomputer,however,additionisnotnecessarilyassociative.Becauseoftheinternalmechanismthatthecomputerusestostorenumbersof
infiniteprecisioninafixednumberofbits,someroundingerroroccursineverymathematicalcalculation.Normally,theseerrorsaresmallenoughthat
wedon'tneedtoworryaboutthem,andtheyoftencanceleachotherout.Butinmanycasesthepropagationofthiserrorleadstovastlydifferent
resultswhentheorderoftheoperationsischanged.
Ifyou'reperformingsensitivenumericalanalysis,beawarethatthetricksofthissectionmayleadtounacceptableerrorpropagationandincorrect
answers.

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 Analysis and Transformations


Toassistourparallelizingtechniques,wecananalyzethealgorithmsoftheloopitselfinsteadofjustanalyzingthevariablesintheloop.Inthemajorityofthe
cases,thereislittlewecandowithoutredesigningthealgorithm,butinafewsituationswecanquicklymodifythecodewithoutacompleteredesign.By
implementingsimpletransformationsontheoriginalcode,wemaybeabletousethetechniquesdiscussedsofartothreadtheloop.

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.

AWARNING ABO UT INNERLO O PS


Priortothreadinganyloop,weshouldalwaysexaminethatloop.Weshouldnotthreadtheloopifitexecutesinaveryshortperiodoftime.Forthese
cases,theoverheadinthesetupandteardownofthethreadedloopmaybegreaterthananyspeedgainedfromthreadingtheloop.
Whenmovingfromtheouterlooptotheinnerloop,wemustexaminetheinnerloop.Justbecausetheouterloopisacandidateforthreadingdoesnot
meantheinnerloopisacandidateforthreading.Ifthenumberofiterationsintheouterloopismanytimeshigherthantheinnerloop,theinnerloopmay
executeonlyforashortperiodoftime.Therecouldalsobemethodcallsintheouterloop,andnotintheinnerloop,thataretakingalongperiodoftime
toexecute.

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.

T HEEF F ECT O F T HEVIRT UALMACHINE


Oneofthefactorsthatcanaffectthescalabilityofaparticularprogramistheimplementationofthevirtualmachineitself.Obtainingasynchronization
lock,forinstance,takesacertainamountoftime,andthecodeinthevirtualmachinethatactuallyimplementsthesynchronizationisoften
synchronizeditself.Twothreadsattemptingtoobtaindifferentsynchronizationlocksmaystillcompeteforaresourcewithinthevirtualmachine.And
thereareotherexampleswherethevirtualmachineoroperatingsystemaffectsthescalabilityofaprogram.
TheresultsthatwepresentinthischapterarebasedontheJ2SE5.0Beta1releasebySunMicrosystems.Theyaredrasticallydifferent(andbetter)
thanresultswe'vepresentedinpreviouseditionsofthisbook.Mostnotably,inthefirstandsecondeditionofthisbook,resultswerebasedonthe1.1.6
productionreleaseoftheJavavirtualmachinefromSunMicrosystems.Inthoseresults,theamountofscalingobservedwasfarlessduetotwo
factors:theoverallslowerexecutionofthecodeandthemuchlongertimerequiredtoobtainasynchronizationlock(evenintheuncontendedcase).

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.

A Simple Loop Test


Inthisexample,we'llexplorehowvariousloophandlersaffectparallelization.Asabaseline,wetakethemeasurementofthisclass:
packagejavathreads.examples.ch15.example10;

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.

A Reduction Variable Test


Whateffectdoesareductionvariablehaveinourtesting?Inournextseriesoftests(example11intheonlinearchive),werewriteourtestssothateverytime
wecalculatealookupvalue,weaddthatvaluetoasumValueinstancevariable.Usingthereductiontechniqueweshowedearlier,themodifiedtestgenerates
thenumbersgiveninTable152.

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.

A Small Inner-Loop Test


Whatifwehadthreadedonlytheinnerloop?Thisquestionisinterestingsinceitdemonstratestheeffectofsynchronizationoverheadversustheamountof
savingsweobtainiftheinnerloopissmall.Asshowninexample12intheonlinearchive,werewriteourfirsttest(withnoreductionvariable)sothatno
loopinterchangeisperformedandtheinnerloopisthreadedinstead,whichproducestheresultsinTable153.
Table153.Scalabilityofinnerloophandlers

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.

AppendixA.Superseded Threading Utilities


Readersofpreviouseditionsofthisbookwillhavenoticedthatmanyoftheclasseswedevelopedforthoseeditionshavebeenreplaced.Thereasonhastodo
withthemanynewclassesprovidedbyJ2SE5.0.PriortoJ2SE5.0,developerswerelefttocreateorpurchasealibrarythatprovidedthehighlevelthreading
supportneededbymorecomplexprograms.Whiletheselibrariescanstillbeused,itisrecommendedthatprogramsmigratetothecoreJ2SE5.0classes
sincethatleavesonelesslibrarytomaintain,test,anddownloadduringexecution.
Whiletheexamplesinthepreviouseditionofthisbookarenowobsolete,thereareafewadvantagestoincludingtheminthisappendix(andintheonline
source).Theexamplesweredesignedtoteachthesubjectofthreading.Theyweredesignedtobesimplistic,notloadedwithfeatures,andspecificallytargeta
particularsubject.MostofthosesubjectsarenowdiscussedinrelationtothenewclassesinJ2SE5.0,andtherestofthemarenolongernecessarysincewe
arenolongermaintainingourownlibrary.Still,forresearchpurposes,thereisadvantageinexaminingthem.
Asthisbookgoestopress,J2SE5.0isonlyabetarelease,somanydeveloperscannotyetusethenewclassesinJ2SE5.0.Thosedeveloperswillalsofind
theseclassesuseful.
Soforthosewhomaybeinterested,hereisaquickreviewofourobsoleteclasses.Obviously,learningtheexamplesinthisappendixisoptional.Usingthese
toolsshouldbeconsideredonlyifyoumustuseavirtualmachineearlierthanJ2SE5.0.

The BusyFlag Class


We'llstartwithaBusyFlagclass:
packagejavathreads.examples.appa;

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.

The CondVar Class


HereisanimplementationoftheCondVarclass:
packagejavathreads.examples.appa;

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.

The Barrier Class


HereisanimplementationoftheBarrierclass:
packagejavathreads.examples.appa;

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.

The RWLock Class


HereisanimplementationoftheRWLock(reader/writerlock)class:
packagejavathreads.examples.appa;

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.

The ThreadPool Class


HereisanimplementationoftheThreadPoolclass:
packagejavathreads.examples.appa;

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.

The JobScheduler Class


HereisanimplementationoftheJobSchedulerclasstoexecuteatask:
packagejavathreads.examples.appa;

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.

The DaemonLock Class


OurjobschedulerclassdependsontheDaemonLockclass.ThepurposeoftheDaemonLockclassistoallowthejobschedulertoshutdowngracefully.
Themainthreadshouldexitwithoutshuttingdownthejobschedulerabruptly:iftherearescheduledtasks,wewantthemtocomplete.Whenthejob
schedulerhasfinishedallitstasks,wewanttheprogramtoexit.

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

About the Authors

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

S-ar putea să vă placă și