Я читав багатопоточність на Java, і я стикався з цим
Локальні змінні безпечні для потоку в Java.
З тих пір я думав, як / чому місцеві змінні безпечні для потоку.
Може хтось, будь ласка, дайте мені знати.
Я читав багатопоточність на Java, і я стикався з цим
Локальні змінні безпечні для потоку в Java.
З тих пір я думав, як / чому місцеві змінні безпечні для потоку.
Може хтось, будь ласка, дайте мені знати.
Відповіді:
Коли ви створюєте потік, у нього буде створений власний стек. Два потоки матимуть два стеки, і один потік ніколи не ділиться своїм стеком з іншим потоком.
Усім локальним змінним, визначеним у вашій програмі, буде виділено пам'ять у стеку (як зауважив Ятін, тут пам'ять означає, посилальне значення для об'єктів та значення для примітивних типів) (Кожен виклик методу потоком створює фрейм стека у власному стеку). Як тільки виконання методу завершиться цим потоком, кадр стека буде видалений.
На YouTube є чудова лекція професора Стенфорда, яка може допомогти вам зрозуміти цю концепцію.
Локальні змінні зберігаються у власному стеку кожного потоку. Це означає, що локальні змінні ніколи не діляться між потоками. Це також означає, що всі локальні примітивні змінні безпечні для потоку.
public void someMethod(){
long threadSafeInt = 0;
threadSafeInt++;
}
Місцеві посилання на об’єкти дещо відрізняються. Само посилання не є спільним. Однак об’єкт, на який посилається, не зберігається в локальному стеку кожного потоку. Всі об'єкти зберігаються у спільній купі. Якщо об’єкт, створений локально, ніколи не уникає методу, в якому він був створений, це безпечно для потоку. Насправді ви також можете передавати його іншим методам та об'єктам, якщо жоден з цих методів або об'єктів не робить переданий об'єкт доступним для інших потоків
Подумайте про такі методи, як визначення функціональності. Коли два потоки запускають один і той же метод, вони жодним чином не пов’язані. Вони кожен створять свою власну версію кожної локальної змінної і не зможуть взаємодіяти між собою жодним чином.
Якщо змінні не є локальними (як змінні екземпляра, визначені за межами методу на рівні класу), тоді вони приєднуються до екземпляра (не до одного запуску методу). У цьому випадку два потоки, що виконують один і той же метод, бачать одну змінну, і це не є безпечним для потоків.
Розглянемо ці два випадки:
public class NotThreadsafe {
int x = 0;
public int incrementX() {
x++;
return x;
}
}
public class Threadsafe {
public int getTwoTimesTwo() {
int x = 1;
x++;
return x*x;
}
}
У першому два потоки, що працюють на одному екземплярі NotThreadsafe
, побачать однаковий х. Це може бути небезпечно, оскільки нитки намагаються змінити x! У другому, два потоки, що працюють на одному екземплярі Threadsafe
, бачитимуть абсолютно різні змінні і не можуть впливати один на одного.
Кожне виклик методу має свої локальні змінні, і, очевидно, виклик методу відбувається в одному потоці. Змінна, яка оновлюється лише одним потоком, за своєю суттю є безпечною для потоків.
Однак уважно стежте за тим, що саме мається на увазі під цим: лише записи в саму змінну безпечні для потоків; виклик методів для об'єкта, на який він посилається, за своєю суттю не є безпечним для потоків . Те саме стосується і безпосереднього оновлення змінних об’єкта.
На додаток до інших відповідей, таких як Намбарі.
Я хотів би зазначити, що ви можете використовувати локальну змінну в методі анонімного типу:
Цей метод можна викликати в інших потоках, які можуть порушити безпеку потоків, тому java змушує всі локальні змінні, що використовуються в аноймових типах, оголошуватися як остаточні.
Розглянемо цей незаконний код:
public void nonCompilableMethod() {
int i=0;
for(int t=0; t<100; t++)
{
new Thread(new Runnable() {
public void run() {
i++; //compile error, i must be final:
//Cannot refer to a non-final variable i inside an
//inner class defined in a different method
}
}).start();
}
}
Якби Java це дозволила (як це робить C # через "закриття"), локальна змінна більше не була б безпечною за будь-яких обставин. У цьому випадку значення i
в кінці всіх потоків не гарантується 100
.
Потік матиме власний стек. Два потоки матимуть два стеки, і один потік ніколи не ділиться своїм стеком з іншим потоком. Локальні змінні зберігаються у власному стеку кожного потоку. Це означає, що локальні змінні ніколи не діляться між потоками.
В основному в Java є чотири типи сховищ для зберігання інформації та даних класу:
Область методів, купа, стек JAVA, ПК
отже, область методу та куча спільно використовуються усіма потоками, але кожен потік має свій власний стек JAVA та ПК, що не є спільним для інших потоків.
Кожен метод у Java є як фрейм стека. отже, коли один метод викликається потоком, фрейм стека завантажується в його стек JAVA. Усі локальні змінні, що знаходяться в цьому фреймі стека та пов'язаний стек операндів, не діляться іншими. ПК матиме інформацію про наступну інструкцію для виконання в байт-коді методу. тому всі локальні змінні БЕЗПЕЧНО.
@Weston також дав хорошу відповідь.
У стеці потоків зберігаються лише локальні змінні.
Локальна змінна, яка є primitive type
(наприклад, int, long ...), зберігається в thread stack
і, як результат - інший потік не має до неї доступу.
Локальна змінна, яка є reference type
(наступницею Object
), складається з 2-х частин - адреси (яка зберігається thread stack
) та об’єкта (яка зберігається heap
)
class MyRunnable implements Runnable() {
public void run() {
method1();
}
void method1() {
int intPrimitive = 1;
method2();
}
void method2() {
MyObject1 myObject1 = new MyObject1();
}
}
class MyObject1 {
MyObject2 myObject2 = new MyObject2();
}
class MyObject2 {
MyObject3 myObject3 = MyObject3.shared;
}
class MyObject3 {
static MyObject3 shared = new MyObject3();
boolean b = false;
}