JavaRush /Java Blog /Random-KO /상위 50개 Java Core 인터뷰 질문 및 답변. 3부
Roman Beekeeper
레벨 35

상위 50개 Java Core 인터뷰 질문 및 답변. 3부

Random-KO 그룹에 게시되었습니다
상위 50개 Java Core 인터뷰 질문 및 답변. 1부 상위 50개 Java Core 인터뷰 질문 및 답변. 2 부

멀티스레딩

37. Java에서 새 스레드(흐름)를 생성하는 방법은 무엇입니까?

어떤 식으로든 Thread 클래스를 사용하여 생성이 발생합니다. 하지만 여기에는 옵션이 있을 수 있습니다.
  1. 우리는 다음으로부터 상속받습니다java.lang.Thread
  2. java.lang.Runnable객체가 생성자 Thread클래스를 허용하는 인터페이스를 구현합니다.
그들 각각에 대해 이야기합시다.

Thread 클래스에서 상속받습니다.

이 작업을 수행하기 위해 우리 클래스에서는 에서 상속받습니다 java.lang.Thread. run()여기에는 우리에게 꼭 필요한 meth가 포함되어 있습니다 . 새 스레드의 모든 수명과 논리는 이 메서드에 포함됩니다. main이것은 새로운 스레드에 대한 일종의 방법입니다 . 그 후에 남은 것은 클래스의 객체를 생성하고 메소드를 실행하는 것입니다 start(). 그러면 새 스레드가 생성되고 그 안에 작성된 논리가 실행됩니다. 한번 보자:
/**
* Пример того, How создавать треды путем наследования {@link Thread} класса.
*/
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();
   }
}
콘솔에 대한 출력은 다음과 같습니다.

Thread-1
Thread-0
Thread-2
즉, 여기에서도 스레드가 차례로 실행되는 것이 아니라 JVM이 결정한 대로 실행되는 것을 볼 수 있습니다.

Runnable 인터페이스 구현

상속을 반대하거나 이미 다른 클래스 중 하나를 상속받은 경우 java.lang.Runnable. run()여기 우리 클래스에서는 해당 예제에서처럼 이 인터페이스를 구현하고 메서드를 구현합니다 . 더 많은 객체를 생성하기만 하면 됩니다 Thread. 더 많은 라인이 더 나쁜 것 같습니다. 그러나 우리는 상속이 얼마나 해로운지 알고 있으며 반드시 상속을 피하는 것이 더 낫다는 것을 알고 있습니다. ;) 살펴보겠습니다:
/**
* Пример того, How создавать треды из интерфейса {@link Runnable}.
* Здесь проще простого - реализуем этот интерфейс и потом передаем в конструктор
* экземпляр реализуемого an object.
*/
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();
   }
}
그리고 실행 결과는 다음과 같습니다.

Thread-0
Thread-1
Thread-2

38. 프로세스와 스레드의 차이점은 무엇입니까?

상위 50개 Java Core 인터뷰 질문 및 답변.  파트 3 - 1프로세스와 스레드에는 다음과 같은 차이점이 있습니다.
  1. 실행 중인 프로그램을 프로세스라고 하며, 스레드는 프로세스의 하위 집합입니다.
  2. 프로세스는 독립적인 반면 스레드는 프로세스의 하위 집합입니다.
  3. 프로세스는 메모리에서 서로 다른 주소 공간을 갖고 있는 반면 스레드는 공통 주소 공간을 포함합니다.
  4. 프로세스에 비해 스레드 간 컨텍스트 전환이 더 빠릅니다.
  5. 프로세스 간 통신은 스레드 간 통신보다 속도가 느리고 비용이 더 많이 듭니다.
  6. 상위 프로세스의 변경 사항은 하위 프로세스에 영향을 미치지 않지만 상위 스레드의 변경 사항은 하위 스레드에 영향을 미칠 수 있습니다.

39. 멀티스레딩의 장점은 무엇입니까?

상위 50개 Java Core 인터뷰 질문 및 답변.  파트 3 - 2
  1. 멀티스레딩을 사용하면 애플리케이션/프로그램이 이미 일부 백그라운드 작업을 실행 중이더라도 항상 입력에 응답할 수 있습니다.
  2. 멀티스레딩을 사용하면 스레드가 독립적으로 실행되므로 작업을 더 빠르게 완료할 수 있습니다.
  3. 멀티스레딩은 스레드가 공통 메모리 리소스를 공유하므로 더 나은 캐시 활용도를 제공합니다.
  4. 멀티스레딩은 하나의 서버가 여러 스레드를 동시에 실행할 수 있기 때문에 필요한 서버의 양을 줄입니다.

40. 스레드의 수명주기에는 어떤 상태가 있습니까?

상위 50개 Java Core 인터뷰 질문 및 답변.  파트 3 - 3
  1. New: 이 상태에서는 new 연산자를 사용하여 클래스 객체가 Thread생성되지만 스레드가 존재하지 않습니다. 스레드는 우리가 호출할 때까지 시작되지 않습니다 start().
  2. Runnable: 이 상태에서는 메서드를 호출한 후 스레드를 실행할 준비가 되었습니다. 시작(). 그러나 아직 스레드 스케줄러에 의해 선택되지 않았습니다.
  3. 실행 중: 이 상태에서 스레드 스케줄러는 준비 상태에서 스레드를 선택하고 실행합니다.
  4. 대기 중/차단됨: 이 상태에서는 스레드가 실행 중이 아니지만 여전히 살아 있거나 다른 스레드가 완료되기를 기다리고 있습니다.
  5. 데드/종료됨: 메서드가 종료되면 run()스레드는 종료되거나 데드 상태가 됩니다.

41. 스레드를 두 번 시작할 수 있습니까?

아니요, 스레드가 시작되고 실행되면 Dead 상태가 되기 때문에 스레드를 다시 시작할 수 없습니다. 따라서 스레드를 두 번 실행하려고 하면 RuntimeException " java.lang.IllegalThreadStateException "이 발생합니다. 한번 보자:
class DoubleStartThreadExample extends Thread {

   /**
    * Имитируем работу треда
    */
   public void run() {
	// что-то происходит. Для нас не существенно на этом этапе
   }

   /**
    * Запускаем тред дважды
    */
   public static void main(String[] args) {
       DoubleStartThreadExample doubleStartThreadExample = new DoubleStartThreadExample();
       doubleStartThreadExample.start();
       doubleStartThreadExample.start();
   }
}
작업이 동일한 스레드의 두 번째 시작에 도달하자마자 예외가 발생합니다. 직접 해보세요 ;) 백 번 듣는 것보다 한 번 보는 것이 더 좋습니다.

42. start() 메서드를 호출하지 않고 run() 메서드를 직접 호출하면 어떻게 되나요?

예, run()물론 메소드를 호출할 수 있지만 새 스레드가 생성되어 별도의 스레드로 실행되지는 않습니다. 이 경우 간단한 메서드를 호출하는 간단한 개체입니다. 방법에 대해 이야기하고 있다면 start()그것은 다른 문제입니다. 이 메소드를 실행하면 runtime새로운 메소드가 실행되고, 차례로 우리의 메소드가 실행됩니다. ;) 제 말을 믿을 수 없다면 다음을 시도해 보세요:
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();

       // просто будут вызваны в потоке main два метода, один за другим.
       runExample1.run();
       runExample2.run();
   }
}
콘솔에 대한 출력은 다음과 같습니다.

0123401234
Thread가 생성되지 않은 것을 볼 수 있습니다. 모든 것이 일반 수업처럼 작동했습니다. 먼저 첫 번째 클래스 방법이 작동했고 그 다음에는 두 번째 클래스 방법이 작동했습니다.

43. 데몬 스레드란 무엇입니까?

상위 50개 Java Core 인터뷰 질문 및 답변.  파트 3 - 4데몬 스레드 (이하 데몬 스레드)는 다른 스레드와 관련하여 백그라운드에서 작업을 수행하는 스레드이다. 즉, 다른 (메인) 스레드와 연계해서만 수행해야 하는 보조 작업을 수행하는 것입니다. Garbage Collector, Finalizer 등과 같이 자동으로 작동하는 데몬 스레드가 많이 있습니다.

Java가 데몬 스레드를 닫는 이유는 무엇입니까?

데몬 스레드의 유일한 목적은 백그라운드 지원 작업을 위해 사용자 스레드에 서비스를 제공하는 것입니다. 따라서 기본 스레드가 완료되면 런타임은 자동으로 모든 데몬 스레드를 닫습니다.

Thread 클래스에서 작업하는 방법

이 클래스는 java.lang.Thread스레드 데몬 작업을 위한 두 가지 방법을 제공합니다.
  1. public void setDaemon(boolean status)- 데몬 스레드가 될 것임을 나타냅니다. 기본값은 입니다 false. 이는 별도로 지정하지 않는 한 데몬이 아닌 스레드가 생성됨을 의미합니다.
  2. public boolean isDaemon()daemon- 본질적으로 이것은 이전 방법을 사용하여 설정한 변수에 대한 getter입니다 .
예:
class DaemonThreadExample extends Thread {

   public void run() {
       // Проверяет, демон ли этот поток or нет
       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();

       // теперь thread1 - поток-демон.
       thread1.setDaemon(true);

       System.out.println("демон?.. " + thread1.isDaemon());
       System.out.println("демон?.. " + thread2.isDaemon());
       System.out.println("демон?.. " + thread3.isDaemon());

       thread1.start();
       thread2.start();
       thread3.start();
   }
}
콘솔 출력:

демон?.. true
демон?.. false
демон?.. false
daemon thread
user thread
user thread
출력에서 우리는 스레드 자체 내부에서 정적 currentThread()메소드를 사용하여 한편으로는 그것이 어떤 스레드인지 알아낼 수 있고, 다른 한편으로는 이 스레드의 객체에 대한 참조가 있으면 알아낼 수 있다는 것을 알 수 있습니다. 그것에서 직접. 이는 구성에 필요한 유연성을 제공합니다.

44. 스레드가 생성된 후 스레드를 데몬으로 만드는 것이 가능합니까?

아니요. 이렇게 하면 예외가 발생합니다 IllegalThreadStateException. 따라서 데몬 스레드가 시작되기 전에만 생성할 수 있습니다. 예:
class SetDaemonAfterStartExample extends Thread {

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

   public static void main(String[] args) {
       SetDaemonAfterStartExample afterStartExample = new SetDaemonAfterStartExample();
       afterStartExample.start();

       // здесь будет выброшено исключение
       afterStartExample.setDaemon(true);
   }
}
콘솔 출력:

Working...
Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.setDaemon(Thread.java:1359)
	at SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

45. 셧다운훅(shutdownhook)이란 무엇입니까?

Shutdownhook은 JVM(Java Virtual Machine)이 종료되기 전에 암시적으로 호출되는 스레드입니다. 따라서 Java Virtual Machine이 정상적으로 또는 갑자기 종료될 때 이를 사용하여 리소스를 정리하거나 상태를 저장할 수 있습니다. shutdown hook다음 방법을 사용하여 추가할 수 있습니다 .
Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
예제에 표시된 대로:
/**
* Программа, которая показывает How запустить shutdown hook тред,
* который выполнится аккурат до окончания работы JVM
*/
class ShutdownHookThreadExample extends Thread {

   public void run() {
       System.out.println("shutdown hook задачу выполнил");
   }

   public static void main(String[] args) {

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

       System.out.println("Теперь программа засыпает, нажмите ctrl+c чтоб завершить ее.");
       try {
           Thread.sleep(60000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
콘솔 출력:

Теперь программа засыпает, нажмите ctrl+c чтоб завершить ее.
shutdown hook задачу выполнил

46. ​​​​동기화란 무엇인가요?

Java의 동기화는 공유 리소스에 대한 여러 스레드의 액세스를 제어하는 ​​기능입니다. 여러 스레드가 동일한 작업을 수행하려고 하면 잘못된 결과가 나올 가능성이 있기 때문에 이를 극복하기 위해 자바에서는 한 번에 하나의 스레드만 작업할 수 있는 동기화를 사용한다. 동기화는 세 가지 방법으로 수행할 수 있습니다.
  • 동기화 방법
  • 특정 블록을 동기화하여
  • 정적 동기화

메소드 동기화

동기화된 방법은 공유 리소스에 대한 개체를 잠그는 데 사용됩니다. 스레드가 동기화된 메서드를 호출하면 자동으로 해당 개체에 대한 잠금을 획득하고 스레드가 작업을 완료하면 잠금을 해제합니다. 이를 작동시키려면 동기화 키워드를 추가해야 합니다 . 예제를 통해 이것이 어떻게 작동하는지 살펴보겠습니다.
/**
* Пример, где мы синхронизируем метод. То есть добавляем ему слово synchronized.
* Есть два писателя, которые хотят использовать один принтер. Они подготовor свои поэмы
* И конечно же не хотят, чтоб их поэмы перемешались, а хотят, чтоб работа была сделана по * * * очереди для каждого из них
*/
class Printer {

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

   public static void main(String args[]) {
       // один an object для двух тредов
       Printer printer  = new Printer();

       // создаем два треда
       Writer1 writer1 = new Writer1(printer);
       Writer2 writer2 = new Writer2(printer);

       // запускаем их
       writer1.start();
       writer2.start();
   }
}

/**
* Писатель номер 1, который пишет свою поэму.
*/
class Writer1 extends Thread {
   Printer printer;

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

   public void run() {
       List<string> poem = Arrays.asList("Я ", this.getName(), " Пишу", " Письмо");
       printer.print(poem);
   }

}

/**
* Писатель номер 2, который пишет свою поэму.
*/
class Writer2 extends Thread {
   Printer printer;

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

   public void run() {
       List<String> poem = Arrays.asList("Не Я ", this.getName(), " Не пишу", " Не Письмо");
       printer.print(poem);
   }
}
그리고 콘솔로의 출력은 다음과 같습니다.

Я Thread-0 Пишу Письмо
Не Я Thread-1 Не пишу Не Письмо

동기화 블록

동기화된 블록은 특정 메서드 리소스에 대한 동기화를 수행하는 데 사용될 수 있습니다. 큰 방법(예, 예, 그런 것을 작성할 수는 없지만 때로는 그런 일이 발생함)에서 어떤 이유로든 작은 부분만 동기화해야 한다고 가정해 보겠습니다. 메소드의 모든 코드를 동기화 블록에 넣으면 동기화 메소드와 동일하게 작동합니다. 구문은 다음과 같습니다.
synchronized (“an object для блокировки”) {
   // сам code, который нужно защитить
}
이전 예제를 반복하지 않기 위해 익명 클래스를 통해 스레드를 생성합니다. 즉, Runnable 인터페이스를 즉시 구현합니다.
/**
* Вот How добавляется блок синхронизации.
* Внутри нужно указать у кого будет взят мьютекс для блокировки.
*/
class Printer {

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

   public static void main(String args[]) {
       // один an object для двух тредов
       Printer printer = new Printer();

       // создаем два треда
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Я ", "Writer1", " Пишу", " Письмо");
               printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Не Я ", "Writer2", " Не пишу", " Не Письмо");
               printer.print(poem);
           }
       });

       // запускаем их
       writer1.start();
       writer2.start();
   }
}

}
그리고 콘솔로 출력

Я Writer1 Пишу Письмо
Не Я Writer2 Не пишу Не Письмо

정적 동기화

정적 메서드를 동기화하면 잠금은 개체가 아닌 클래스에 설정됩니다. 이 예에서는 정적 동기화를 수행하기 위해 정적 메서드에 동기화 키워드를 적용합니다.
/**
* Вот How добавляется блок синхронизации.
* Внутри нужно указать у кого будет взят мьютекс для блокировки.
*/
class Printer {

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

   public static void main(String args[]) {

       // создаем два треда
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Я ", "Writer1", " Пишу", " Письмо");
               Printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Не Я ", "Writer2", " Не пишу", " Не Письмо");
               Printer.print(poem);
           }
       });

       // запускаем их
       writer1.start();
       writer2.start();
   }
}
콘솔로의 출력은 다음과 같습니다.

Не Я Writer2 Не пишу Не Письмо
Я Writer1 Пишу Письмо

47. 휘발성 변수란 무엇입니까?

키워드는 volatile다중 스레드 프로그래밍에서 스레드 안전성을 제공하는 데 사용됩니다. 변경 가능한 하나의 변수에 대한 수정 사항이 다른 모든 스레드에 표시되어 한 번에 하나의 스레드에서 하나의 변수를 사용할 수 있기 때문입니다. 키워드를 사용하면 volatile변수가 스레드로부터 안전하고 공유 메모리에 저장되며 스레드가 해당 변수를 캐시로 가져가지 않도록 보장할 수 있습니다. 그것은 어떻게 생겼나요?
private volatile AtomicInteger count;
변수에 추가하기만 하면 됩니다 volatile. 그러나 이것이 완전한 스레드 안전성을 의미하는 것은 아닙니다. 결국 작업은 변수에 대해 원자적이지 않을 수 있습니다. Atomic그러나 작업을 원자적으로 수행하는 클래스, 즉 프로세서의 한 번의 실행을 사용할 수 있습니다 . 이러한 클래스는 패키지에서 많이 찾을 수 있습니다 java.util.concurrent.atomic.

48. 교착상태란 무엇인가

Java의 교착 상태는 멀티스레딩의 일부입니다. 한 스레드가 다른 스레드가 획득한 개체 잠금을 기다리고 있고, 두 번째 스레드가 첫 번째 스레드가 획득한 개체 잠금을 기다리는 상황에서 교착 상태가 발생할 수 있습니다. 따라서 이 두 스레드는 서로를 기다리고 코드 실행을 계속하지 않습니다. 상위 50개 Java Core 인터뷰 질문 및 답변.  파트 3 - 5Runnable을 구현하는 클래스가 있는 예제를 살펴보겠습니다. 생성자에서 두 개의 리소스를 허용합니다. run() 메서드 내에서 하나씩 잠금을 수행하므로 이 클래스의 두 개체를 생성하고 리소스를 다른 순서로 전송하면 쉽게 잠금이 발생할 수 있습니다.
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();
   }
}

/**
* Класс, который принимает два ресурса.
*/
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() + " захватил ресурс: " + r1);

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

           synchronized (r2) {
               System.out.println(Thread.currentThread().getName() + " захватил ресурс: " + r2);
           }
       }
   }
}
콘솔 출력:

Первый тред захватил первый ресурс
Второй тред захватывает второй ресурс

49. 교착상태를 피하는 방법은 무엇입니까?

교착 상태가 어떻게 발생하는지 알고 있는 내용을 바탕으로 몇 가지 결론을 도출할 수 있습니다.
  • 위의 예에서 볼 수 있듯이 교착 상태는 잠금 중첩으로 인해 발생했습니다. 즉, 하나의 자물쇠 안에는 다른 자물쇠가 하나 이상 있습니다. 다음과 같은 방법으로 이를 방지할 수 있습니다. 중첩하는 대신 새 추상화를 맨 위에 추가하고 잠금을 더 높은 수준에 부여하고 중첩된 잠금을 제거해야 합니다.
  • 차단이 많을수록 교착 상태가 발생할 가능성이 커집니다. 따라서 자물쇠를 추가할 때마다 그것이 정말 필요한지, 새로 추가하는 것을 피할 수 있는지 생각해 볼 필요가 있습니다.
  • . Thread.join()_ 한 스레드가 다른 스레드를 기다리고 있는 경우에도 교착 상태가 발생할 수 있습니다. join()이 문제를 방지하려면 메서드 에 시간 제한을 설정하는 것이 좋습니다 .
  • 스레드가 하나라면 교착 상태가 발생하지 않습니다. ;)

50. 경쟁 조건이란 무엇입니까?

실제 경주에서 자동차가 성능을 발휘한다면 멀티스레딩이라는 경주 용어에서 스레드는 경주에서 성능을 발휘합니다. 그런데 왜? 실행 중인 두 개의 스레드가 있으며 동일한 개체에 액세스할 수 있습니다. 동시에 상태를 업데이트하려고 시도할 수도 있습니다. 지금까지 모든 것이 명확했습니다. 그렇죠? 따라서 스레드는 실제 병렬(프로세서에 코어가 두 개 이상인 경우)로 작동하거나 프로세서가 짧은 시간을 할당할 때 조건부 병렬로 작동합니다. 그리고 우리는 이러한 프로세스를 제어할 수 없으므로 한 스레드가 객체에서 데이터를 읽을 때 다른 스레드가 읽기 전에 이를 변경할 시간이 있다고 보장할 수 없습니다. 이와 같은 문제는 테스트와 실행의 조합이 진행될 때 발생합니다. 무슨 뜻이에요? if예를 들어, 조건 자체가 변경되는 본문의 표현식이 있습니다 .
int z = 0;

// проверь
if (z < 5) {
//действуй
   z = z + 5;
}
따라서 z가 여전히 0일 때 두 스레드가 동시에 이 코드 블록을 입력하고 함께 이 값을 변경하는 상황이 있을 수 있습니다. 그리고 결국 우리는 기대값인 5가 아니라 10을 얻게 될 것입니다. 이것을 피하는 방법은 무엇입니까? 실행 전후에 잠금이 필요합니다. 즉, 첫 번째 스레드가 블록에 들어가려면 if모든 작업을 수행하고 이를 변경한 z후에만 다음 스레드에게 이를 수행할 수 있는 기회를 제공합니다. 그러나 다음 스레드는 이미 5와 같기 if때문에 블록에 들어가지 않습니다 .z
// получить блокировку для z
if (z < 5) {
   z = z + 5;
}
// выпустить из блокировки z
===================================================

출력 대신

끝까지 읽어주신 모든 분들께 감사하다는 말씀 전하고 싶습니다. 긴 여정이었는데 성공하셨네요! 모든 것이 명확하지는 않습니다. 이건 괜찮아. Java를 배우기 시작하자마자 정적 변수가 무엇인지 머리를 감쌀 수 없었습니다. 하지만 아무것도, 나는 이 생각으로 잠을 자고 몇 가지 출처를 더 읽고 마침내 이해했습니다. 인터뷰를 준비하는 것은 실제적인 것보다 학문적인 문제에 가깝습니다. 따라서 각 인터뷰 전에 자주 사용하지 않는 내용을 반복하고 기억을 새로 고쳐야 합니다.

언제나 그렇듯이 유용한 링크는 다음과 같습니다.

읽어주신 모든 분들께 감사드립니다. 곧 만나요) GitHub의 내 프로필
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION