Поведение статического метода в многопоточной среде на 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отправленные несколькими потоками?

namalfernandolk
источник
На какой язык вы ориентируетесь?
ΩmegaMan
3
@ OmegaMan: это java
намалфернандолк

Ответы:

192

Ответ Ханса Пассанта хорош. Но я подумал, что попытаюсь объяснить на более простом уровне для всех, кто сталкивается с этим и плохо знаком с Java. Поехали..

Память в java делится на два типа - кучу и стеки. Куча - это место, где живут все объекты, а стеки - это место, где потоки выполняют свою работу. Каждый поток имеет свой собственный стек и не может получить доступ к стекам друг друга. У каждого потока также есть указатель на код, который указывает на бит кода, который они выполняют в данный момент.

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

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

Обратите внимание, что сон - это то, что поток делает сам с собой.

Зелиг
источник
3
Итак, в среде многоядерного процессора может быть несколько потоков, выполняющих один и тот же код одновременно, не так ли? А в однопроцессорной среде в данный момент времени выполняется только один поток. (несколько потоков разделяют время между собой.) Итак, когда расписание потока дает возможность от текущего выполняющего потока (A) к потоку (B), как поток (A) возобновляется с того места, где он был приостановлен? Я имею в виду, откуда он знает точку возобновления? Это из-за того, что «Каждый поток также имеет указатель на код, который указывает на бит кода, который они в настоящее время выполняют?» как вы сказали?
namalfernandolk
6
У тебя вышло. Просто чтобы прояснить некоторые моменты - во-первых, то, как распределяются потоки, находится вне контроля Java. Я говорю о JVM Sun Hotspot. JVM отображает поток Java в поток ОС, и ОС решает, какие потоки запускать. Как вы говорите, на одноядерной машине ОС может запускаться только по одной, но на многоядерной машине она может запускать более одной одновременно. Во-вторых, поток на самом деле не знает, когда он приостанавливается, единственная информация, которую он имеет, - это указатель программы (указатель на код) и стек, которые сохраняются и восстанавливаются точно так же, как и были.
selig
Итак, межпотоковая интерференция происходит, когда потоки используют переменные вне своей локальной области видимости и, например, один поток обновляет значение переменной до того, как другой поток вытянет эту переменную (или указатель на переменную) в свой собственный стек? Это правильное понимание?
hariszhr 06
2
Чтобы быть немного более точным ... вмешательство может произойти и может произойти, но не обязательно произойдет ... когда потоки совместно используют что-то в куче (нелокально). Есть несколько различных способов возникновения помех, которые зависят от зависимостей между различными частями кода. Я не уверен в вашем примере, поскольку отсутствуют некоторые детали. Возможно, вы имеете в виду потенциальную проблему, когда потоки A и B оба читают общее значение, а затем оба обновляют его на основе считанного значения. Это гонка за данными.
selig
1
@selig, если у меня есть класс только с методами экземпляра для ex: класс службы и это singleton, тогда мне не нужно беспокоиться о нескольких потоках, выполняющих методы экземпляра одновременно, поскольку класс службы не содержит состояния, в котором состояние присутствует, только если у класса есть переменные экземпляра. Я правильно понимаю?
Юг Сингх
67

Он перейдет в состояние сна?

Нет, выполнение потока не влияет на другие потоки, если они намеренно не синхронизируются друг с другом. Если у вас более одного ядра процессора, как у всех последних машин, эти потоки, вероятно, будут выполняться в одно и то же время. Это становится немного менее вероятным, когда вы запускаете 5 потоков, поскольку на вашей машине может не хватать ядер. Операционная система вынуждена выбирать между ними, давая каждому время для работы. Работа планировщика потоков. Тогда поток не будет находиться в «спящем» состоянии, он просто приостановлен и ждет, пока планировщик потоков не даст ему возможность запуститься. Он возобновится с того места, где он был прерван планировщиком.

Есть ли возможность поменять местами inFileStr, отправляемую несколькими потоками?

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

Нет такой гарантии, если аргумент является ссылкой на другой вид изменяемого объекта. Или если сам метод использует статические переменные или ссылки на объекты в куче. Синхронизация требуется, когда поток изменяет объект, а другой поток читает его. Замок ключевое слово в языке С # это шаблонный способ реализовать такую необходимую синхронизацию. Тот факт, что метод статичен , не означает, что такая синхронизация никогда не требуется. Это менее вероятно, поскольку вам не нужно беспокоиться о потоках, обращающихся к одному и тому же объекту (разделяя его ).

Ганс Пассан
источник
3
К сожалению, не видел тега [java]. Достаточно близко.
Hans Passant
Я забыл добавить, когда это было опубликовано. Виноват. :). В любом случае спасибо за ответ. Это было очень полезно.
namalfernandolk