Почему в цикле можно создать объект потока с тем же именем переменной?

#java #multithreading #for-loop #jvm

Вопрос:

Я понимаю, что если я создам некоторую переменную внутри цикла в главном потоке, она создаст ее, затем на следующей итерации она создаст другую с тем же именем и будет продолжаться до тех пор, пока цикл не будет завершен. Нет проблем, потому что на каждой итерации созданная переменная исчезает, поэтому мы можем создать еще одну.

Но что, если я создам потоки с тем же именем в цикле. Потоки не «завершаются» одновременно. Так почему же это возможно? Я чувствую, что мне следует отделить работу JVM с созданием переменных и работу ОС с обработкой потоков, но я хочу услышать правильное объяснение.

 for (int i = 0; i < 10; i  ) {
    MyFirstThread thread = new MyFirstThread();
    thread.start();
}
 

Комментарии:

1. Сам объект может все еще существовать, но переменная , которая ссылается на него, вышла из области видимости. Область видимости-это концепция, которая имеет смысл только во время компиляции, а не во время выполнения.

2. Имена переменных доступны для вашего удобства. Они не существуют во время выполнения.

3. @Michael Я не думаю, что проблема операции связана с областью действия, я думаю, что дело в разнице между переменными и ссылочными объектами. Таким образом, связанный вопрос может оказаться бесполезным.

4. @RalfKleberhoff Согласен, я передумал

5. Обратите внимание, что вы даже можете сделать это без какой-либо переменной вообще, и пусть тело цикла будет просто new MyFirstThread().start() для того же эффекта.

Ответ №1:

Вы смешиваете объекты (экземпляры классов) и переменные. Это две совершенно разные вещи в Java.

Вы можете создавать объекты с new помощью оператора, как в new MyFirstThread() . С этого момента они существуют «вечно», пока сборщик мусора не обнаружит, что они больше не нужны, что для потока не произойдет до его завершения.

Переменная может содержать ссылку на объект. И до тех пор, пока на объект ссылается переменная, сборщик мусора не будет его трогать.

Теперь в вашем коде

 for (int i = 0; i < 10; i  ) {
    MyFirstThread thread = new MyFirstThread();
    thread.start();
}
 

Допустимая (но упрощенная) интерпретация состоит в том, что вы десять раз

  • создайте переменную с именем thread ,
  • создайте объект класса MyFirstThread и сохраните ссылку на этот объект в переменной,
  • запустите эту тему,
  • избавьтесь от переменной thread (когда выполнение завершится в } конце итерации = конец области действия переменной).

Ключевым моментом является то, что удаление переменной не влияет на объект, на который она ссылается. До тех пор, пока у этого объекта есть причина продолжать свое существование (например, поток все еще работает), он будет оставаться живым и продолжать выполнять свою работу.

Во время цикла, например, на второй итерации, первая thread переменная больше не существует, но первый MyFirstThread экземпляр все еще существует и выполняется.

Аналогия:

Представьте, что экземпляры MyFirstThread-это дома, а переменные-листы бумаги, на которых вы указываете адрес дома.

Затем вы делаете это десять раз:

  • возьмите чистый лист бумаги,
  • постройте дом в каком-нибудь месте и запишите адрес на листе бумаги,
  • используя адрес из вашего листка, прикажите кому-нибудь непрерывно косить газон (извините, не идеальная аналогия).,
  • выбросьте лист бумаги.

В конце концов, там будет десять домов с идеальными газонами, но вы не сможете получить к ним доступ, так как больше не знаете, как их найти.

Комментарии:

1.На случай, если опу не все равно,… Thread Причина, по которой экземпляр не собирается во время выполнения потока, проста: вызов метода не является нижней частью стека потока. run() Какая-то функция в JVM вызывает run() , и у этого вызывающего абонента есть локальная переменная, содержащая ссылку на Thread.currentThread() экземпляр. Локальная переменная вызывающего объекта продолжает существовать, по крайней мере, до тех пор, пока run() не вернется или не возникнет исключение.

2. @IngvarKokorin Здесь все еще, кажется, есть какое-то неправильное представление, поэтому я немного расширю свой ответ.

3. Это не отличается, например for (int i = 0; i < 10; i ) { Frame f = new Frame(); f.setVisible(true); } , от . Некоторые операции, например метод start() Thread или setVisible из Frame , могут привести к тому, что на объект будет ссылаться служба, предоставляемая самой средой выполнения. Существуют методы, такие как Thread.getAllStackTraces().keySet() или Frame.getFrames() , которые позволяют находить эти все еще активные объекты, даже если у вас еще нет локальной ссылки на них.

4. @IngvarKokorin, Прочтите, что Ральф добавил к своему ответу-немного о домах и листках бумаги. Ответ: «Это не та же самая ссылка…» Это все равно что сказать, что карточка, которую я вам дал с адресом дома, не такая же, как та, которую я дал кому-то другому. Хорошо, карты не одинаковые, но они оба относятся к одному и тому же дому. В Java, если у меня в программе есть Thread t =... , а затем, Thread u = t; , то да, t и u это разные переменные, но они оба относятся к одному и тому же Thread экземпляру. GC не восстановит этот экземпляр до тех пор, пока не останется переменных, которые ссылаются на него.

5. @Holger по аналогии с моим домом, использование статических Thread методов означало бы обратиться в какой-нибудь местный орган власти и спросить их о домах, принадлежащих мне.

Ответ №2:

Вы просто назначаете ссылку на новый пользовательский объект MyFirstThread новой переменной thread . В каждом цикле цикла переменная thread выходит за рамки, и вы теряете ссылку на MyFirstThread созданную и новую переменную thread .