java synchronized what is thread synchronization java
Denna handledning förklarar trådsynkronisering i Java tillsammans med relaterade begrepp som Java Lock, Race Condition, Mutex, Java Volatile & Deadlock i Java:
I en multitrådningsmiljö där flera trådar är inblandade kommer det säkert att bli kollisioner när mer än en tråd försöker få samma resurs samtidigt. Dessa sammanstötningar resulterar i ”rasförhållanden” och därmed ger programmet oväntade resultat.
Till exempel, en enda fil uppdateras av två trådar. Om en tråd T1 håller på att uppdatera den här filen, säg någon variabel. Medan den här uppdateringen av T1 fortfarande pågår, låt oss säga att den andra tråden T2 också uppdaterar samma variabel. På detta sätt ger variabeln fel resultat.
=> Se upp hela Java-träningsserien här.
När flera trådar är inblandade bör vi hantera dessa trådar på ett sådant sätt att en resurs kan nås av en enda tråd åt gången. I exemplet ovan bör filen som nås av båda trådarna hanteras på ett sådant sätt att T2 inte kan komma åt filen förrän T1 har gjort åtkomst till den.
Detta görs i Java med ' Trådsynkronisering ”.
Vad du kommer att lära dig:
- Trådsynkronisering i Java
- Multitrådning utan synkronisering
- Multitrådning med synkronisering
- Slutsats
Trådsynkronisering i Java
Eftersom Java är ett språk med flera trådar har trådsynkronisering mycket betydelse i Java eftersom flera trådar körs parallellt i en applikation.
Vi använder nyckelord “Synkroniserad” och 'flyktig' för att uppnå synkronisering i Java
Vi behöver synkronisering när det delade objektet eller resursen kan ändras. Om resursen är oföränderlig läser trådarna bara resursen antingen samtidigt eller individuellt.
I det här fallet behöver vi inte synkronisera resursen. I det här fallet ser JVM till det Java-synkroniserad kod körs av en tråd i taget .
För det mesta kan samtidig åtkomst till delade resurser i Java införa fel som 'Memory inconsistency' och 'thread interference'. För att undvika dessa fel måste vi gå för synkronisering av delade resurser så att tillgången till dessa resurser är ömsesidigt exklusiv.
Vi använder ett koncept som kallas Övervakar för att implementera synkronisering. En skärm kan nås med endast en tråd åt gången. När en tråd får låset kan vi säga att tråden har gått in i bildskärmen.
När en bildskärm nås av en viss tråd låses skärmen och alla andra trådar som försöker komma in i skärmen är avstängda tills åtkomsttråden är klar och släpper låset.
Framöver kommer vi att diskutera synkronisering i Java i detalj i denna handledning. Låt oss nu diskutera några grundläggande begrepp relaterade till synkronisering i Java.
Race-tillstånd i Java
I en multitrådad miljö, när mer än en tråd försöker komma åt en delad resurs för att skriva samtidigt, tävlar flera trådar varandra för att slutföra åtkomsten till resursen. Detta ger upphov till ”rasvillkor”.
En sak att tänka på är att det inte är något problem om flera trådar försöker komma åt en delad resurs endast för läsning. Problemet uppstår när flera trådar får åtkomst till samma resurs samtidigt.
Tävlingsförhållanden uppstår på grund av brist på korrekt synkronisering av trådar i programmet. När vi ordentligt synkroniserar trådarna så att bara en tråd åt gången kommer åt resursen, och rasförhållandet upphör att existera.
Så hur upptäcker vi rasförhållandet?
Det bästa sättet att upptäcka tävlingsförhållanden är genom kodgranskning. Som programmerare bör vi granska koden noggrant för att kontrollera om potentiella tävlingsförhållanden kan uppstå.
Lås / bildskärmar i Java
Vi har redan nämnt att vi använder skärmar eller lås för att implementera synkronisering. Bildskärmen eller låset är en intern enhet och associeras med varje objekt. Så när en tråd behöver komma åt objektet måste den först skaffa lås eller övervakning av sitt föremål, arbeta på objektet och sedan släppa låset.
Lås i Java ser ut som visas nedan:
public class Lock { private boolean isLocked = false; public synchronized void lock() throws InterruptedException { while(isLocked) { wait(); } isLocked = true; } public synchronized void unlock(){ isLocked = false; notify(); } } Som visas ovan har vi en lås () -metod som låser förekomsten. Alla trådar som anropar lock () -metoden kommer att blockeras tills unblock () -metoduppsättningarna är låsta flagga till false och meddelar alla väntande trådar.
Några tips att komma ihåg om lås:
- I Java har varje objekt ett lås eller en bildskärm. Detta lås kan nås med en tråd.
- På en gång kan bara en tråd skaffa den här skärmen eller låsa.
- Java-programmeringsspråket ger ett nyckelord Synkroniserat ”som gör att vi kan synkronisera trådarna genom att göra ett block eller en metod som synkroniserad.
- De delade resurserna som trådarna behöver komma åt hålls under detta synkroniserade block / metod.
Mutexes i Java
Vi har redan diskuterat att i en flertrådad miljö kan rasförhållanden uppstå när mer än en tråd försöker komma åt de delade resurserna samtidigt och tävlingsförhållandena resulterar i oväntad produktion.
Den del av programmet som försöker komma åt den delade resursen heter “Kritisk sektion” . För att undvika uppkomst av tävlingsförhållanden finns det ett behov av att synkronisera tillgången till den kritiska delen. Genom att synkronisera detta kritiska avsnitt ser vi till att endast en tråd kan komma åt det kritiska avsnittet åt gången.
Den enklaste typen av synkroniserare är ”mutex”. Mutex säkerställer att vid en given instans kan endast en tråd utföra det kritiska avsnittet.
Mutex liknar begreppet bildskärmar eller lås som vi diskuterade ovan. Om en tråd behöver komma åt ett kritiskt avsnitt måste den förvärva mutex. När mutex har förvärvats kommer tråden att få åtkomst till den kritiska sektionskoden och när du är klar släpper den mutex.
De andra trådarna som väntar på att få åtkomst till det kritiska avsnittet kommer att blockeras under tiden. Så snart tråden som håller mutex släpper den, kommer en annan tråd in i det kritiska avsnittet.
hur man blir testare för produkter
Det finns flera sätt på vilka vi kan implementera en mutex i Java.
- Använda synkroniserat nyckelord
- Använda Semaphore
- Använda ReentrantLock
I denna handledning kommer vi att diskutera det första tillvägagångssättet, dvs Synkronisering. De andra två tillvägagångssätten - Semaphore och ReentrantLock kommer att diskuteras i nästa handledning där vi kommer att diskutera Java-paketet.
Synkroniserat nyckelord
Java tillhandahåller nyckelordet 'Synkroniserat' som kan användas i ett program för att markera ett kritiskt avsnitt. Det kritiska avsnittet kan vara ett kodblock eller en komplett metod. Således kan endast en tråd komma åt det kritiska avsnittet som markeras med det synkroniserade nyckelordet.
Vi kan skriva samtidiga delar (delar som körs samtidigt) för en applikation med det synkroniserade nyckelordet. Vi blir också av med tävlingsförhållandena genom att göra ett kodblock eller en metod synkroniserad.
När vi markerar ett block eller en metod synkroniserad skyddar vi de delade resurserna i dessa enheter från samtidig åtkomst och därmed korruption.
Typer av synkronisering
Det finns två typer av synkronisering enligt nedan:
# 1) Processynkronisering
Processynkronisering involverar flera processer eller trådar som körs samtidigt. De når slutligen ett tillstånd där dessa processer eller trådar förbinder sig till en specifik sekvens av åtgärder.
# 2) Trådsynkronisering
I trådsynkronisering försöker mer än en tråd komma åt ett delat utrymme. Trådarna synkroniseras på ett sådant sätt att det delade utrymmet endast nås av en tråd i taget.
Processynkroniseringen omfattas inte av denna handledning. Därför kommer vi bara att diskutera trådsynkronisering här.
I Java kan vi använda det synkroniserade nyckelordet med:
- Ett kodblock
- En metod
Ovanstående typer är ömsesidigt uteslutande typer av trådsynkronisering. Ömsesidig uteslutning hindrar trådarna som har åtkomst till delad data från att störa varandra.
Den andra typen av trådsynkronisering är 'InterThread-kommunikation' som bygger på samarbete mellan trådar. Intertrådskommunikation omfattas inte av denna handledning.
Innan vi fortsätter med synkronisering av block och metoder, låt oss implementera ett Java-program för att visa trådarnas beteende när det inte finns någon synkronisering.
Multitrådning utan synkronisering
Följande Java-program har flera trådar som inte är synkroniserade.
class PrintCount { //method to print the thread counter public void printcounter() { try { for(int i = 5; i > 0; i--) { System.out.println('Counter ==> ' + i ); } } catch (Exception e) { System.out.println('Thread interrupted.'); } } } //thread class class ThreadCounter extends Thread { private Thread t; private String threadName; PrintCount PD; //class constructor for initialization ThreadCounter( String name, PrintCount pd) { threadName = name; PD = pd; } //run method for thread public void run() { PD.printcounter(); System.out.println('Thread ' + threadName + ' exiting.'); } //start method for thread public void start () { System.out.println('Starting ' + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class Main { public static void main(String args()) { PrintCount PD = new PrintCount(); //create two instances of thread class ThreadCounter T1 = new ThreadCounter( 'ThreadCounter_1 ', PD ); ThreadCounter T2 = new ThreadCounter( 'ThreadCounter_2 ', PD ); //start both the threads T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch ( Exception e) { System.out.println('Interrupted'); } } }Produktion

Från utgången kan vi se att eftersom trådarna inte synkroniseras är utdata inkonsekventa. Båda trådarna startar och sedan visar de räknaren efter varandra. Båda trådarna går ut i slutet.
Från det givna programmet borde den första tråden ha avslutats efter att räknarvärdena har visats, och sedan bör den andra tråden ha börjat visa räknarvärdena.
Låt oss nu gå till synkronisering och börja med kodblocksynkronisering.
Synkroniserat kodblock
Ett synkroniserat block används för att synkronisera ett kodblock. Detta block består vanligtvis av några få rader. Ett synkroniserat block används när vi inte vill att en hel metod ska synkroniseras.
Till exempel, vi har en metod med säg 75 rader kod. Av detta krävs endast 10 rader kod för att köras av en tråd åt gången. I det här fallet, om vi gör hela metoden som synkroniserad, kommer det att vara en börda för systemet. I sådana situationer går vi efter synkroniserade block.
Räckvidden för den synkroniserade metoden är alltid mindre än för en synkroniserad metod. En synkroniserad metod låser ett objekt från en delad resurs som ska användas av flera trådar.
Den allmänna syntaxen för ett synkroniserat block är som visas nedan:
synchronized (lock_object){ //synchronized code statements }Här är 'lock_object' ett objektreferensuttryck som låset ska erhållas på. Så när en tråd vill komma åt de synkroniserade uttalandena inuti blocket för exekvering, måste den skaffa låset på 'lock_object' -monitorn.
Som redan diskuterats säkerställer det synkroniserade nyckelordet att endast en tråd kan få ett lås åt gången och alla andra trådar måste vänta tills tråden som håller låset är klar och släpper låset.
Notera
- Ett “NullPointerException” kastas om lock_object som används är Null.
- Om en tråd sover medan du fortfarande håller låset, frigörs inte låset. De andra trådarna kommer inte att kunna komma åt det delade objektet under denna vilotid.
Nu kommer vi att presentera ovanstående exempel som redan implementerades med små förändringar. I det tidigare programmet synkroniserade vi inte koden. Nu ska vi använda det synkroniserade blocket och jämföra utdata.
Multitrådning med synkronisering
I Java-programmet nedan använder vi ett synkroniserat block. I körmetoden synkroniserar vi koden för rader som skriver ut räknaren för varje tråd.
class PrintCount { //print thread counter public void printCounter() { try { for(int i = 5; i > 0; i--) { System.out.println('Counter ==> ' + i ); } } catch (Exception e) { System.out.println('Thread interrupted.'); } } } //thread class class ThreadCounter extends Thread { private Thread t; private String threadName; PrintCount PD; //class constructor for initialization ThreadCounter( String name, PrintCount pd) { threadName = name; PD = pd; } //run () method for thread with synchronized block public void run() { synchronized(PD) { PD.printCounter(); } System.out.println('Thread ' + threadName + ' exiting.'); } //start () method for thread public void start () { System.out.println('Starting ' + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class Main { public static void main(String args()) { PrintCount PD = new PrintCount(); //create thread instances ThreadCounter T1 = new ThreadCounter( 'Thread_1 ', PD ); ThreadCounter T2 = new ThreadCounter( 'Thread_2 ', PD ); //start both the threads T1.start(); T2.start(); // wait for threads to end try { T1.join(); T2.join(); } catch ( Exception e) { System.out.println('Interrupted'); } } }Produktion

Nu är resultatet av detta program med synkroniserat block ganska konsekvent. Som förväntat börjar båda trådarna köras. Den första tråden slutade visa räknarvärdena och avsluta. Sedan visar den andra tråden räknarvärdena och avslutas.
Synkroniserad metod
Låt oss diskutera den synkroniserade metoden i det här avsnittet. Tidigare har vi sett att vi kan förklara ett litet block bestående av färre kodrader som ett synkroniserat block. Om vi vill att hela funktionen ska synkroniseras kan vi förklara en metod som synkroniserad.
När en metod görs synkroniserad kan bara en tråd göra ett metodanrop åt gången.
Den allmänna syntaxen för att skriva en synkroniserad metod är:
synchronized method_name (parameters){ //synchronized code } Precis som ett synkroniserat block, när det gäller en synkroniserad metod, behöver vi ett lock_object som kommer att användas av trådar som får åtkomst till den synkroniserade metoden.
För den synkroniserade metoden kan låsobjektet vara något av följande:
- Om den synkroniserade metoden är statisk ges låsobjektet av '.class' -objektet.
- För en icke-statisk metod ges låsobjektet av det aktuella objektet, dvs. 'detta' objekt.
En speciell egenskap hos det synkroniserade nyckelordet är att det återkommer. Detta innebär att en synkroniserad metod kan ringa en annan synkroniserad metod med samma lås. Så en tråd som håller låset kan komma åt en annan synkroniserad metod utan att behöva anskaffa ett annat lås.
Den synkroniserade metoden demonstreras med hjälp av exemplet nedan.
class NumberClass { //synchronized method to print squares of numbers synchronized void printSquares(int n) throws InterruptedException { //iterate from 1 to given number and print the squares at each iteration for (int i = 1; i <= n; i++) { System.out.println(Thread.currentThread().getName() + ' :: '+ i*i); Thread.sleep(500); } } } public class Main { public static void main(String args()) { final NumberClass number = new NumberClass(); //create thread Runnable thread = new Runnable() { public void run() { try { number.printSquares(3); } catch (InterruptedException e) { e.printStackTrace(); } } }; //start thread instance new Thread(thread, 'Thread One').start(); new Thread(thread, 'Thread Two').start(); } } Produktion

hur man kör .jar-filer
I programmet ovan har vi använt en synkroniserad metod för att skriva ut kvadraterna för ett tal. Den övre gränsen för numret skickas till metoden som ett argument. Från och med 1 skrivs rutorna för varje nummer ut tills den övre gränsen uppnås.
I huvudfunktionen skapas trådinstansen. Varje trådinstans skickas ett nummer för att skriva ut rutor.
Som nämnts ovan, när en metod som ska synkroniseras är statisk, är låsobjektet involverat i klassen och inte objektet. Det betyder att vi kommer att låsa på klassen och inte på objektet. Detta kallas statisk synkronisering.
Ett annat exempel ges nedan.
class Table{ //synchronized static method to print squares of numbers synchronized static void printTable(int n){ for(int i=1;i<=10;i++){ System.out.print(n*i + ' '); try{ Thread.sleep(400); }catch(Exception e){} } System.out.println(); } } //thread class Thread_One class Thread_One extends Thread{ public void run(){ Table.printTable(2); } } //thread class Thread_Two class Thread_Two extends Thread{ public void run(){ Table.printTable(5); } } public class Main{ public static void main(String t()){ //create instances of Thread_One and Thread_Two Thread_One t1=new Thread_One (); Thread_Two t2=new Thread_Two (); //start each thread instance t1.start(); t2.start(); } } Produktion

I ovanstående program skriver vi ut multiplikationstabeller med siffror. Varje nummer vars tabell ska skrivas ut är en trådinstans av olika trådklass. Således skriver vi ut multiplikationstabeller på 2 & 5, så vi har två klasser 'thread_one och thread_two för att skriva ut tabellerna 2 respektive 5.
Sammanfattningsvis utför det Java-synkroniserade nyckelordet följande funktioner:
- Det synkroniserade nyckelordet i Java garanterar ömsesidig exklusiv tillgång till delade resurser genom att tillhandahålla en låsmekanism. Låsning förhindrar också tävlingsförhållanden.
- Med hjälp av det synkroniserade nyckelordet förhindrar vi samtidiga programmeringsfel i koden.
- När en metod eller ett block deklareras som synkroniserat behöver en tråd ett exklusivt lås för att komma in i den synkroniserade metoden eller blocket. Efter att ha utfört nödvändiga åtgärder släpper tråden låset och spolar skrivningen. På så sätt elimineras minnesfel relaterade till inkonsekvens.
Flyktig i Java
Ett flyktigt nyckelord i Java används för att göra klasser trådsäkra. Vi använder också det flyktiga nyckelordet för att ändra variabelvärdet med olika trådar. Ett flyktigt nyckelord kan användas för att deklarera en variabel med både primitiva typer och objekt.
I vissa fall används ett flyktigt nyckelord som ett alternativ för det synkroniserade nyckelordet men notera att det inte ersätter det synkroniserade nyckelordet.
När en variabel förklaras flyktig cachas dess värde aldrig utan läses alltid från huvudminnet. En flyktig variabel garanterar beställning och synlighet. Även om en variabel kan förklaras som flyktig kan vi inte förklara klasser eller metoder som flyktiga.
Tänk på följande kodblock:
class ABC{ static volatile int myvar =10; }I ovanstående kod är variabeln myvar statisk och flyktig. En statisk variabel delas mellan alla klassobjekten. Den flyktiga variabeln finns alltid i huvudminnet och cachelagras aldrig.
Därför finns det bara en kopia av myvar i huvudminnet och alla läs- / skrivåtgärder kommer att göras på denna variabel från huvudminnet. Om myvar inte förklarades som flyktigt skulle varje trådobjekt ha en annan kopia som skulle resultera i inkonsekvenser.
Några av skillnaderna mellan flyktiga och synkroniserade sökord listas nedan.
| Flyktiga nyckelord | Synkroniserat nyckelord |
|---|---|
| Det flyktiga nyckelordet används endast med variabler. | Det synkroniserade nyckelordet används med kodblock och metoder. |
| Ett flyktigt nyckelord kan inte blockera tråden för att vänta. | Det synkroniserade nyckelordet kan blockera tråden för att vänta. |
| Trådprestanda förbättras med Volatile. | Trådprestanda försämras något med synkroniserad. |
| Flyktiga variabler finns i huvudminnet. | Synkroniserade konstruktioner finns inte i huvudminnet. |
| Volatile synkroniserar en variabel mellan trådminnet och huvudminnet åt gången. | Synkroniserat nyckelord synkroniserar alla variabler samtidigt. |
Dödläge i Java
Vi har sett att vi kan synkronisera flera trådar med hjälp av synkroniserat nyckelord och göra program trådsäkra. Genom att synkronisera trådarna ser vi till att flera trådar körs samtidigt i en miljö med flera trådar.
Ibland uppstår dock en situation där trådar inte längre kan fungera samtidigt. Istället väntar de oändligt. Detta inträffar när en tråd väntar på en resurs och den resursen blockeras av den andra tråden.
Den andra tråden väntar å andra sidan på den resurs som blockeras av den första tråden. En sådan situation ger upphov till ”dödläge” i Java.
Dödläge i Java visas med bilden nedan.

Som vi kan se från ovanstående diagram har tråd A låst resursen r1 och väntar på resursen R2. Tråd B, å andra sidan, har blockerat resurs r2 och väntar på r1.
Således kan ingen av trådarna slutföra körningen om de inte tar tag i de väntande resurserna. Denna situation har resulterat i dödläget där båda trådarna väntar oändligt på resurserna.
Nedan ges ett exempel på Deadlocks i Java.
public class Main { public static void main(String() args) { //define shared resources final String shared_res1 = 'Java tutorials'; final String shared_res2 = 'Multithreading'; // thread_one => locks shared_res1 then shared_res2 Thread thread_one = new Thread() { public void run() { synchronized (shared_res1) { System.out.println('Thread one: locked shared resource 1'); try { Thread.sleep(100);} catch (Exception e) {} synchronized (shared_res2) { System.out.println('Thread one: locked shared resource 2'); } } } }; // thread_two=> locks shared_res2 then shared_res1 Thread thread_two = new Thread() { public void run() { synchronized (shared_res2) { System.out.println('Thread two: locked shared resource 2'); try { Thread.sleep(100);} catch (Exception e) {} synchronized (shared_res1) { System.out.println('Thread two: locked shared resource 1'); } } } }; //start both the threads thread_one.start(); thread_two.start(); } }Produktion

I programmet ovan har vi två delade resurser och två trådar. Båda trådarna försöker komma åt de delade resurserna en efter en. Utgången visar att båda trådarna låser en resurs vardera medan de väntar på de andra. Därigenom skapar en dödläge.
Även om vi inte kan förhindra att dödläge uppstår helt, kan vi verkligen undvika dem genom att ta några steg.
Nedan listas de medel som vi kan undvika blockeringar i Java.
# 1) Genom att undvika kapslade lås
Att ha kapslade lås är den viktigaste anledningen till att ha blockeringar. Kapslade lås är lås som ges till flera trådar. Således bör vi undvika att låsa mer än en tråd.
# 2) Använd tråd Gå med
Vi bör använda Thread.join med maximal tid så att trådarna kan använda maximal tid för körning. Detta kommer att förhindra dödläge som oftast inträffar när en tråd kontinuerligt väntar på andra.
# 3) Undvik onödigt lås
Vi bör bara låsa den nödvändiga koden. Att ha onödiga lås för koden kan leda till blockeringar i programmet. Eftersom blockeringar kan bryta koden och hindra flödet av programmet bör vi vara benägna att undvika blockeringar i våra program.
Vanliga frågor
F # 1) Vad är synkronisering och varför är det viktigt?
Svar: Synkronisering är processen för att kontrollera åtkomsten till en delad resurs till flera trådar. Utan synkronisering kan flera trådar uppdatera eller ändra den delade resursen samtidigt vilket resulterar i inkonsekvenser.
Därför bör vi säkerställa att trådarna synkroniseras i en miljö med flera trådar så att de får tillgång till de delade resurserna ömsesidigt exklusivt och konsekvent.
F # 2) Vad är synkronisering och icke-synkronisering i Java?
Svar: Synkronisering betyder att en konstruktion är trådsäker. Det betyder att flera trådar inte kan komma åt konstruktionen (kodblock, metod, etc.) på en gång.
Icke-synkroniserade konstruktioner är inte trådsäkra. Flera trådar kan komma åt icke-synkroniserade metoder eller block när som helst. En populär icke-synkroniserad klass i Java är StringBuilder.
F # 3) Varför krävs synkronisering?
Svar: När processer måste köras samtidigt behöver vi synkronisering. Detta beror på att vi behöver resurser som kan delas mellan många processer.
För att undvika kollisioner mellan processer eller trådar för åtkomst till delade resurser måste vi synkronisera dessa resurser så att alla trådar får tillgång till resurser och applikationen går också smidigt.
F # 4) Hur får du en synkroniserad ArrayList?
Svar: Vi kan använda Collections.synchronized list method med ArrayList som ett argument för att konvertera ArrayList till en synkroniserad lista.
F # 5) Är HashMap synkroniserad?
vilket lager av osi-modellen fungerar med ramar?
Svar: Nej, HashMap är inte synkroniserat men HashTable är synkroniserat.
Slutsats
I denna handledning har vi diskuterat synkronisering av trådar i detalj. Tillsammans med det lärde vi oss också om det flyktiga nyckelordet och blockeringar i Java. Synkronisering består av process- och trådsynkronisering.
I en multithreading-miljö är vi mer intresserade av trådsynkronisering. Vi har sett det synkroniserade nyckelordsmetoden för trådsynkronisering här.
Dödläge är en situation där flera trådar ändlöst väntar på resurser. Vi har sett exemplet på blockeringar i Java tillsammans med metoderna för att undvika blockeringar i Java.
=> Besök här för att lära dig Java från grunden.
Rekommenderad läsning
- Thread.Sleep () - Thread Sleep () Metod i Java med exempel
- Java-trådar med metoder och livscykel
- Java Basics: Java Syntax, Java Class och Core Java Concepts
- Multitrådning i Java - Handledning med exempel
- Multitrådning i C ++ med exempel
- JAVA-handledning för nybörjare: 100+ praktiska Java-videohandledning
- Java-komponenter: Java Platform, JDK, JRE och Java Virtual Machine
- Java String Tutorial | Java-strängmetoder med exempel