Статична поведінка методу в багатопотоковому середовищі в Java


114

Існує просте дурне запитання, яке мене турбує і висуває кілька міркувань. Я хочу викинути всі сумніви щодо нижчезазначених питань.

class Clstest{

    public static String testStaticMethod(String inFileStr) {

        // section 0

        // section 1

        // do something with inFileStr

        // section 2

        // section 3

        return inFileStr;

    }

}

Припустимо, що п’ять потоків виконує дзвінок Clstest.testStaticMethod("arg-n")одночасно.

Тема 1 дзвінків Clstest.testStaticMethod("arg-1").

Коли нитка 1 знаходиться в розділі 1, нитка 2 дзвонить Clstest.testStaticMethod("arg-2").

Тоді що буде з Ниткою 1? Чи перейде він у стан сну?

Коли Тема 1 отримає шанс, чи відновить її виконання з розділу 1, де було призупинено?

Як це відбувається, коли один Clstest.testStaticMethodі той же Clstest.testStaticMethodрозділяється між усіма п’ятьма потоками?

Чи є можливість обмінятися inFileStrнадісланим декількома потоками?


На яку мову ви орієнтуєтесь?
ΩmegaMan

3
@ OmegaMan: це java
namalfernandolk

Відповіді:


192

Відповідь Ганса Пасанта хороша. Але я подумав, що спробую пояснити на трохи простішому рівні для всіх, хто стикається з цим і є новинкою для Java. Ось іде ..

Пам'ять у Java розбита на два види - купа і стеки. Купа - це місце, де живуть усі об’єкти, а стопки - там, де нитки виконують свою роботу. Кожен потік має свій стек і не може отримати доступ до стеків один одного. Кожен потік також має вказівник на код, який вказує на біт коду, який вони зараз працюють.

Коли потік починає виконувати новий метод, він зберігає аргументи та локальні змінні в цьому методі на власному стеку. Деякі з цих значень можуть бути вказівниками на об’єкти на купі. Якщо два потоки працюють один і той же метод одночасно, вони будуть мати свої вказівники коду, що вказують на цей метод, і матимуть власні копії аргументів та локальні змінні у своїх стеках. Вони будуть заважати один одному лише в тому випадку, якщо речі, що знаходяться на їхніх стеках, вказують на однакові предмети на купі. У цьому випадку можуть статися всілякі речі. Але, як зазначає Ганс, рядки незмінні (неможливо змінити), тому ми безпечні, якщо це єдиний об'єкт, яким «поділяються».

Так багато потоків можуть працювати одним методом. Вони можуть не працювати одночасно - це залежить від того, скільки ядер у вас на машині, оскільки JVM відображає потоки Java в потоки ОС, які заплановані на апаратні потоки. Отже, ви мало контролюєте спосіб переплетення цих потоків без використання складних механізмів синхронізації .

Зауважте, що спати - це те, що нитка робить для себе.


3
Так, у багатоядерному процесорному середовищі може бути кілька потоків, що працюють один і той же код одночасно, чи не так? І в середовищі одного процесора є лише один потік, який працює в даний момент часу. (декілька потоків, що ділять час між ними.) Отже, коли планувальник потоків дає шанс від поточного потокового виходу (A) до потоку (B), як поновлюється потік (A) з того місця, де він був призупинений? Я маю на увазі, як він знає точку резюме? Це тому, що "Кожен потік також має вказівник на код, який вказує на біт коду, який вони зараз працюють?" як ти сказав?
namalfernandolk

6
Ви це отримали. Просто для уточнення деяких моментів - по-перше, те, як заплановано потоки, знаходиться поза контролем Java. Тут я говорю про СВ Sun's Hotspot JVM. JVM відображає потік Java на потік ОС, і ОС вирішує, які потоки запускати. Як ви кажете, на одноядерній машині ОС може працювати лише одна за одною, але в багатоядерній машині вона може працювати відразу більше. По-друге, потік насправді не усвідомлює, коли він призупиняється, єдина інформація, яку він має, - це покажчик програми (вказівник на код) та стек, які зберігаються та відновлюються саме так, як вони були.
selig

Отже, інтерференція між потоками трапляється, коли потоки використовують змінні поза їх локальною областю, і, наприклад, один потік оновлює значення змінної, перш ніж інший потік витягне цю змінну (або вказівник на змінну) на власний стек? Це правильне розуміння?
hariszhr

2
Якщо бути дещо точнішим ... втручання може відбуватися і може відбуватися, але необов'язково траплятися ... коли нитки діляться речами на купі (не локальній). Існує кілька різних способів втручання, які залежать від залежностей між різними частинами коду. Я не впевнений у вашому прикладі, оскільки деякі деталі відсутні. Можливо, ви посилаєтесь на потенційну проблему, де обидва потоки A і B читають спільне значення, а потім обидва оновляють його на основі значення прочитаного. Це перегони даних.
selig

1
@selig, якщо у мене є клас із лише методами екземпляра для ex: службовий клас, і це одиночний, то мені не потрібно турбуватися про декілька потоків, виконуючи методи екземпляра одночасно, оскільки клас обслуговування не містить жодного стану, де стан існує лише у тому випадку, якщо у класу є змінні екземпляри. Чи правильно я розумію?
Юг Сінгх

67

Чи перейде він у стан сну?

Ні, запуск потоку не впливає на інші потоки, доки вони навмисно не синхронізуються між собою. Якщо у вас є більше одного ядра процесора, як це роблять усі останні машини, ці потоки, ймовірно, виконуються в той самий час. Це стає трохи менше, коли ви запускаєте 5 ниток, оскільки у вашої машини може бути недостатньо ядер. Операційна система змушена вибирати між собою, даючи їм кожен час працювати. Завдання планувальника потоків. Потім потік не знаходитиметься у стані "сну", він просто призупиняється і чекає, коли планувальник потоків дасть йому можливість запуску. Він відновиться там, де його перервав планувальник.

Чи є можливість обміну inFileStr, що надсилається декількома потоками?

Немає такої можливості, потоки мають власний стек, тому будь-який аргумент методу та локальна змінна будуть унікальними для кожного потоку. Крім того, використання рядка гарантує, що ці потоки не можуть заважати один одному, оскільки рядки незмінні.

Не існує такої гарантії, якщо аргумент є посиланням на інший тип об'єкта, що змінюється. Або якщо сам метод використовує змінні, які є статичними або посилаються на об'єкти в купі. Синхронізація потрібна, коли потік модифікує об'єкт, а інший потік читає його. Замок ключове слово в мові С # це шаблонний спосіб реалізувати таку необхідну синхронізацію. Те, що метод є статичним , не означає, що така синхронізація ніколи не потрібна. Лише менш вірогідні, оскільки вам не доведеться турбуватися про те, що звертаються до одного об’єкта (спільний доступ до цього )


3
На жаль, ніколи не бачив тегу [java]. Достатньо близько.
Ганс Пасант

Я забув додати, коли він був розміщений. Моє ліжко. :). Будь-який спосіб дякую за відповідь. Це було дуже корисно.
namalfernandolk
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.