CodeGym /Java-blogg /Tilfeldig /Topp 50 jobbintervjuspørsmål og svar for Java Core. Del 2...
John Squirrels
Nivå
San Francisco

Topp 50 jobbintervjuspørsmål og svar for Java Core. Del 2

Publisert i gruppen
Topp 50 jobbintervjuspørsmål og svar for Java Core. Del 1Topp 50 jobbintervjuspørsmål og svar for Java Core.  Del 2 - 1

Multithreading

24. Hvordan lager jeg en ny tråd i Java?

På en eller annen måte opprettes en tråd ved å bruke trådklassen. Men det er forskjellige måter å gjøre dette på...
  1. Arv java.lang.Thread .
  2. Implementer java.lang.Runnable- grensesnittet — Thread- klassens konstruktør tar et Runnable-objekt.
La oss snakke om hver av dem.

Arv trådklassen

I dette tilfellet får vi klassen vår til å arve java.lang.Thread . Den har en run()- metode, og det er akkurat det vi trenger. Hele livet og logikken til den nye tråden vil være i denne metoden. Det er litt som en hovedmetode for den nye tråden. Etter det gjenstår det bare å lage et objekt av klassen vår og kalle start() -metoden. Dette vil opprette en ny tråd og begynne å utføre logikken. La oss ta en titt:

/**
* An example of how to create threads by inheriting the {@link Thread} class.
*/
class ThreadInheritance extends Thread {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance threadInheritance1 = new ThreadInheritance();
       ThreadInheritance threadInheritance2 = new ThreadInheritance();
       ThreadInheritance threadInheritance3 = new ThreadInheritance();
       threadInheritance1.start();
       threadInheritance2.start();
       threadInheritance3.start();
   }
}
Konsollutgangen vil være noe slikt:
Tråd-1 Tråd-0 Tråd-2
Det vil si at selv her ser vi at tråder ikke kjøres i rekkefølge, men heller slik JVM finner det passende å kjøre dem :)

Implementer Runnable-grensesnittet

Hvis du er imot arv og/eller allerede arver en annen klasse, kan du bruke grensesnittet java.lang.Runnable . Her får vi klassen vår til å implementere dette grensesnittet ved å implementere run()- metoden, akkurat som i eksemplet ovenfor. Alt som gjenstår er å lage trådobjekter . Det ser ut til at flere linjer med kode er verre. Men vi vet hvor skadelig arv er og at det er bedre å unngå det for all del ;) Ta en titt:

/**
* An example of how to create threads from the {@link Runnable} interface.
* It's easier than easy — we implement this interface and then pass an instance of our object
* to the constructor.
*/
class ThreadInheritance implements Runnable {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance runnable1 = new ThreadInheritance();
       ThreadInheritance runnable2 = new ThreadInheritance();
       ThreadInheritance runnable3 = new ThreadInheritance();

       Thread threadRunnable1 = new Thread(runnable1);
       Thread threadRunnable2 = new Thread(runnable2);
       Thread threadRunnable3 = new Thread(runnable3);

       threadRunnable1.start();
       threadRunnable2.start();
       threadRunnable3.start();
   }
}
Og her er resultatet:
Tråd-0 Tråd-1 Tråd-2

25. Hva er forskjellen mellom en prosess og en tråd?

Topp 50 jobbintervjuspørsmål og svar for Java Core.  Del 2 - 2En prosess og en tråd er forskjellige på følgende måter:
  1. Et kjørende program kalles en prosess, men en tråd er en del av en prosess.
  2. Prosesser er uavhengige, men tråder er deler av en prosess.
  3. Prosesser har forskjellige adresserom i minnet, men tråder deler et felles adresseområde.
  4. Kontekstbytte mellom tråder er raskere enn å bytte mellom prosesser.
  5. Kommunikasjon mellom prosesser er tregere og dyrere enn kommunikasjon mellom tråder.
  6. Eventuelle endringer i en overordnet prosess påvirker ikke en underordnet prosess, men endringer i en overordnet tråd kan påvirke en undertråd.

26. Hva er fordelene med multithreading?

  1. Multithreading lar en applikasjon/program alltid være responsiv på input, selv om den allerede kjører noen bakgrunnsoppgaver;
  2. Multithreading gjør det mulig å fullføre oppgaver raskere, fordi tråder kjøres uavhengig;
  3. Multithreading gir bedre bruk av cache-minne, fordi tråder kan få tilgang til delte minneressurser;
  4. Multithreading reduserer antallet servere som kreves, fordi én server kan kjøre flere tråder samtidig.

27. Hva er tilstandene i en tråds livssyklus?

Topp 50 jobbintervjuspørsmål og svar for Java Core.  Del 2 - 3
  1. Ny: I denne tilstanden opprettes Thread- objektet med den nye operatoren, men en ny tråd eksisterer ikke ennå. Tråden starter ikke før vi kaller start() -metoden.
  2. Kjørbar: I denne tilstanden er tråden klar til å kjøre etter start() metode kalles. Den er imidlertid ennå ikke valgt av trådplanleggeren.
  3. Kjører: I denne tilstanden velger trådplanleggeren en tråd fra en klar tilstand, og den kjører.
  4. Venter/blokkert: i denne tilstanden kjører ikke en tråd, men den er fortsatt i live eller venter på at en annen tråd skal fullføres.
  5. Dead/Terminated: når en tråd går ut av run()- metoden, er den i død eller avsluttet tilstand.

28. Er det mulig å kjøre en tråd to ganger?

Nei, vi kan ikke starte en tråd på nytt, for etter at en tråd starter og kjører, går den inn i dødtilstanden. Hvis vi prøver å starte en tråd to ganger, vil en java.lang.IllegalThreadStateException bli kastet. La oss ta en titt:

class DoubleStartThreadExample extends Thread {

   /**
    * Simulate the work of a thread
    */
   public void run() {
	// Something happens. At this state, this is not essential.
   }

   /**
    * Start the thread twice
    */
   public static void main(String[] args) {
       DoubleStartThreadExample doubleStartThreadExample = new DoubleStartThreadExample();
       doubleStartThreadExample.start();
       doubleStartThreadExample.start();
   }
}
Det vil være et unntak så snart utførelse kommer til den andre starten av samme tråd. Prøv selv ;) Det er bedre å se dette en gang enn å høre om det hundre ganger.

29. Hva om du kaller run() direkte uten å kalle start()?

Ja, du kan sikkert kalle run()- metoden, men en ny tråd vil ikke bli opprettet, og metoden vil ikke kjøre på en egen tråd. I dette tilfellet har vi et vanlig objekt som kaller en vanlig metode. Hvis vi snakker om start()- metoden, så er det en annen sak. Når denne metoden kalles, starter JVM en ny tråd. Denne tråden kaller på sin side metoden vår ;) Tror du ikke det? Her, prøv det:

class ThreadCallRunExample extends Thread {

   public void run() {
       for (int i = 0; i < 5; i++) {
           System.out.print(i);
       }
   }

   public static void main(String args[]) {
       ThreadCallRunExample runExample1 = new ThreadCallRunExample();
       ThreadCallRunExample runExample2 = new ThreadCallRunExample();

       // Two ordinary methods will be called in the main thread, one after the other.
       runExample1.run();
       runExample2.run();
   }
}
Og konsollutgangen vil se slik ut:
0123401234
Som du ser ble det ikke opprettet noen tråd. Alt fungerte akkurat som i en vanlig klasse. Først ble metoden til det første objektet utført, og deretter det andre.

30. Hva er en demon-tråd?

En daemon-tråd er en tråd som utfører oppgaver med lavere prioritet enn en annen tråd. Med andre ord er jobben å utføre hjelpeoppgaver som bare må gjøres i forbindelse med en annen (hoved)tråd. Det er mange daemon-tråder som kjører automatisk, for eksempel søppelinnsamling, finalizer, etc.

Hvorfor avslutter Java en daemon-tråd?

Daemon-trådens eneste formål er å gi bakgrunnsstøtte til en brukers tråd. Følgelig, hvis hovedtråden avsluttes, avslutter JVM automatisk alle sine daemon-tråder.

Metoder i trådklassen

Java.lang.Thread - klassen gir to metoder for å jobbe med en daemon-tråd:
  1. public void setDaemon(boolsk status) — Denne metoden indikerer om dette vil være en daemon-tråd. Standarden er falsk . Dette betyr at ingen daemon-tråder vil bli opprettet med mindre du spesifikt sier det.
  2. public boolean isDaemon() — Denne metoden er i hovedsak en getter for daemon- variabelen, som vi satte med den forrige metoden.
Eksempel:

class DaemonThreadExample extends Thread {

   public void run() {
       // Checks whether this thread is a daemon
       if (Thread.currentThread().isDaemon()) {
           System.out.println("daemon thread");
       } else {
           System.out.println("user thread");
       }
   }

   public static void main(String[] args) {
       DaemonThreadExample thread1 = new DaemonThreadExample();
       DaemonThreadExample thread2 = new DaemonThreadExample();
       DaemonThreadExample thread3 = new DaemonThreadExample();

       // Make thread1 a daemon thread.
       thread1.setDaemon(true);

       System.out.println("daemon? " + thread1.isDaemon());
       System.out.println("daemon? " + thread2.isDaemon());
       System.out.println("daemon? " + thread3.isDaemon());

       thread1.start();
       thread2.start();
       thread3.start();
   }
}
Konsoll utgang:
demon? ekte demon? falsk demon? falsk daemon tråd bruker tråd bruker tråd
Fra utgangen ser vi at inne i selve tråden kan vi bruke den statiske currentThread()- metoden for å finne ut hvilken tråd det er. Alternativt, hvis vi har en referanse til trådobjektet, kan vi også finne ut direkte fra det. Dette gir det nødvendige nivået av konfigurerbarhet.

31. Er det mulig å gjøre en tråd til en demon etter at den er opprettet?

Nei. Hvis du prøver å gjøre dette, vil du få en IllegalThreadStateException . Dette betyr at vi bare kan lage en demon-tråd før den starter. Eksempel:

class SetDaemonAfterStartExample extends Thread {

   public void run() {
       System.out.println("Working...");
   }

   public static void main(String[] args) {
       SetDaemonAfterStartExample afterStartExample = new SetDaemonAfterStartExample();
       afterStartExample.start();
      
       // An exception will be thrown here
       afterStartExample.setDaemon(true);
   }
}
Konsoll utgang:
Fungerer... Unntak i tråden "main" java.lang.IllegalThreadStateException på java.lang.Thread.setDaemon(Thread.java:1359) på SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

32. Hva er en shutdown-krok?

En avslutningskrok er en tråd som implisitt kalles før den virtuelle Java-maskinen (JVM) slås av. Dermed kan vi bruke den til å frigjøre en ressurs eller lagre tilstand når den virtuelle Java-maskinen slår seg av normalt eller unormalt. Vi kan legge til en avslutningskrok ved å bruke følgende metode:

Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
Som vist i eksempelet:

/**
* A program that shows how to start a shutdown hook thread,
* which will be executed right before the JVM shuts down
*/
class ShutdownHookThreadExample extends Thread {

   public void run() {
       System.out.println("shutdown hook executed");
   }

   public static void main(String[] args) {

       Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());

       System.out.println("Now the program is going to fall asleep. Press Ctrl+C to terminate it.");
       try {
           Thread.sleep(60000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
Konsoll utgang:
Nå skal programmet sovne. Trykk Ctrl+C for å avslutte. avstengningskrok utført

33. Hva er synkronisering?

I Java er synkronisering muligheten til å kontrollere tilgangen til flere tråder til en hvilken som helst delt ressurs. Når flere tråder prøver å utføre den samme oppgaven samtidig, kan du få et feil resultat. For å fikse dette problemet bruker Java synkronisering, som lar bare én tråd kjøres om gangen. Synkronisering kan oppnås på tre måter:
  • Synkronisering av en metode
  • Synkronisering av en bestemt blokk
  • Statisk synkronisering

Synkronisering av en metode

En synkronisert metode brukes til å låse et objekt for en delt ressurs. Når en tråd kaller en synkronisert metode, får den automatisk objektets lås og slipper den når tråden fullfører oppgaven. For å få dette til å fungere, må du legge til det synkroniserte nøkkelordet. Vi kan se hvordan dette fungerer ved å se på et eksempel:

/**
* An example where we synchronize a method. That is, we add the synchronized keyword to it.
* There are two authors who want to use one printer. Each of them has composed their own poems
* And of course they don’t want their poems mixed up. Instead, they want work to be performed in * * * order for each of them
*/
class Printer {

   synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer  = new Printer();

       // Create two threads
       Writer1 writer1 = new Writer1(printer);
       Writer2 writer2 = new Writer2(printer);

       // Start them
       writer1.start();
       writer2.start();
   }
}

/**
* Author No. 1, who writes an original poem.
*/
class Writer1 extends Thread {
   Printer printer;

   Writer1(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<string> poem = Arrays.asList("I ", this.getName(), " Write", " A Letter");
       printer.print(poem);
   }

}

/**
* Author No. 2, who writes an original poem.
*/
class Writer2 extends Thread {
   Printer printer;

   Writer2(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<String> poem = Arrays.asList("I Do Not ", this.getName(), " Not Write", " No Letter");
       printer.print(poem);
   }
}
Og konsollutgangen er denne:
Jeg tråd-0 Skriv et brev Jeg gjør ikke tråd-1 Skriver ikke noe brev

Synkroniseringsblokk

En synkronisert blokk kan brukes til å utføre synkronisering på en hvilken som helst spesiell ressurs i en metode. La oss si at i en stor metode (ja, du bør ikke skrive dem, men noen ganger skjer de) du trenger å synkronisere bare en liten del av en eller annen grunn. Hvis du legger all metodens kode i en synkronisert blokk, vil den fungere på samme måte som en synkronisert metode. Syntaksen ser slik ut:

synchronized ("object to be locked") {
   // The code that must be protected
}
For å unngå å gjenta det forrige eksempelet, vil vi lage tråder ved hjelp av anonyme klasser, dvs. at vi umiddelbart implementerer Runnable-grensesnittet.

/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   void print(List<String> wordsToPrint) {
       synchronized (this) {
           wordsToPrint.forEach(System.out::print);
       }
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer = new Printer();

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}

}
Og konsollutgangen er denne:
I Writer1 Write A Letter I Don't Writer2 Ikke Skrive No Letter

Statisk synkronisering

Hvis du gjør en statisk metode synkronisert, vil låsingen skje på klassen, ikke objektet. I dette eksemplet utfører vi statisk synkronisering ved å bruke det synkroniserte nøkkelordet på en statisk metode:

/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   static synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               Printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               Printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}
Og konsollutgangen er denne:
Jeg skriver ikke 2 Ikke skriver ingen bokstav I Writer1 Skriver et brev

34. Hva er en flyktig variabel?

I flertrådsprogrammering brukes nøkkelordet volatile for trådsikkerhet. Når en mutbar variabel endres, er endringen synlig for alle andre tråder, slik at en variabel kan brukes av én tråd om gangen. Ved å bruke det flyktige nøkkelordet kan du garantere at en variabel er trådsikker og lagret i delt minne, og at tråder ikke lagrer den i bufferen. Hvordan ser dette ut?

private volatile AtomicInteger count;
Vi legger bare til flyktig til variabelen. Men husk at dette ikke betyr fullstendig trådsikkerhet... Tross alt er operasjoner på variabelen kanskje ikke atomære. Når det er sagt, kan du bruke Atomic- klasser som gjør operasjoner atomisk, altså i en enkelt CPU-instruksjon. Det er mange slike klasser i java.util.concurrent.atomic -pakken.

35. Hva er deadlock?

I Java er dødlås noe som kan skje som en del av multithreading. En vranglås kan oppstå når en tråd venter på et objekts lås ervervet av en annen tråd, og den andre tråden venter på objektets lås ervervet av den første tråden. Dette betyr at de to trådene venter på hverandre, og kjøringen av koden deres kan ikke fortsette. Topp 50 jobbintervjuspørsmål og svar for Java Core.  Del 2 - 4La oss vurdere et eksempel som har en klasse som implementerer Runnable. Dens konstruktør tar to ressurser. Run()-metoden henter låsen for dem i rekkefølge. Hvis du oppretter to objekter av denne klassen, og sender ressursene i en annen rekkefølge, kan du enkelt havne i vranglås:

class DeadLock {

   public static void main(String[] args) {
       final Integer r1 = 10;
       final Integer r2 = 15;

       DeadlockThread threadR1R2 = new DeadlockThread(r1, r2);
       DeadlockThread threadR2R1 = new DeadlockThread(r2, r1);

       new Thread(threadR1R2).start();
       new Thread(threadR2R1).start();
   }
}

/**
* A class that accepts two resources.
*/
class DeadlockThread implements Runnable {

   private final Integer r1;
   private final Integer r2;

   public DeadlockThread(Integer r1, Integer r2) {
       this.r1 = r1;
       this.r2 = r2;
   }

   @Override
   public void run() {
       synchronized (r1) {
           System.out.println(Thread.currentThread().getName() + " acquired resource: " + r1);

           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           synchronized (r2) {
               System.out.println(Thread.currentThread().getName() + " acquired resource: " + r2);
           }
       }
   }
}
Konsoll utgang:
Den første tråden kjøpte den første ressursen Den andre tråden kjøpte den andre ressursen

36. Hvordan unngår du vranglås?

Fordi vi vet hvordan dødlås oppstår, kan vi trekke noen konklusjoner...
  • I eksemplet ovenfor oppstår dødlåsen på grunn av at vi har nestet låsing. Det vil si at vi har en synkronisert blokk inne i en synkronisert blokk. For å unngå dette, i stedet for å neste, må du lage et nytt høyere abstraksjonslag, flytte synkroniseringen til det høyere nivået og eliminere den nestede låsingen.
  • Jo mer låsing du gjør, jo mer sannsynlig vil det være en vranglås. Derfor, hver gang du legger til en synkronisert blokk, må du tenke på om du virkelig trenger den og om du kan unngå å legge til en ny.
  • Bruke Thread.join() . Du kan også havne i vranglås mens en tråd venter på en annen. For å unngå dette problemet kan du vurdere å sette en tidsavbrudd for join() -metoden.
  • Hvis vi har én tråd, blir det ingen fastlåsing ;)

37. Hva er en rasebetingelse?

Hvis virkelige løp involverer biler, så involverer løp i multithreading tråder. Men hvorfor? :/ Det er to tråder som kjører og kan få tilgang til samme objekt. Og de kan forsøke å oppdatere det delte objektets tilstand samtidig. Alt er klart så langt, ikke sant? Tråder utføres enten bokstavelig talt parallelt (hvis prosessoren har mer enn én kjerne) eller sekvensielt, med prosessoren som tildeler interfolierede tidsstykker. Vi kan ikke styre disse prosessene. Dette betyr at når en tråd leser data fra et objekt, kan vi ikke garantere at den har tid til å endre objektet FØR en annen tråd gjør det. Slike problemer oppstår når vi har disse "sjekk-og-handle"-kombinasjonene. Hva betyr det? Anta at vi har en if- setning hvis kropp endrer selve hvis-betingelsen, for eksempel:

int z = 0;

// Check
if (z < 5) {
// Act
   z = z + 5;
}
To tråder kan samtidig gå inn i denne kodeblokken når z fortsatt er null, og da kan begge trådene endre verdien. Som et resultat vil vi ikke få forventet verdi på 5. I stedet ville vi fått 10. Hvordan unngår du dette? Du må skaffe deg en lås før du sjekker og handler, og deretter frigjøre låsen etterpå. Det vil si at du må få den første tråden til å gå inn i if -blokken, utføre alle handlingene, endre z , og først da gi den neste tråden muligheten til å gjøre det samme. Men neste tråd kommer ikke inn i if- blokken, siden z nå vil være 5:

// Acquire the lock for z
if (z < 5) {
   z = z + 5;
}
// Release z's lock
===================================================

I stedet for en konklusjon

Jeg vil si takk til alle som har lest til slutt. Det var langt, men du holdt ut! Kanskje ikke alt er klart. Dette er normalt. Da jeg først begynte å studere Java, klarte jeg ikke å vikle hjernen rundt hva en statisk variabel er. Men ingen stor sak. Jeg sov på den, leste noen flere kilder, og så kom forståelsen. Å forberede seg til et intervju er mer et akademisk spørsmål enn et praktisk. Som et resultat, før hvert intervju, bør du gjennomgå og oppdatere i minnet de tingene du kanskje ikke bruker så ofte.

Og som alltid, her er noen nyttige linker:

Takk alle sammen for at dere leser. Vi sees snart :) Min GitHub-profilTopp 50 jobbintervjuspørsmål og svar for Java Core.  Del 2 - 5
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION