Являются ли несинхронизированные статические методы потокобезопасными, если они не изменяют статические переменные класса?

145

Мне было интересно, если у вас есть статический метод, который не синхронизируется, но не изменяет статические переменные, это потокобезопасно? А что, если метод создает локальные переменные внутри него? Например, является ли следующий код потокобезопасным?

public static String[] makeStringArray( String a, String b ){
    return new String[]{ a, b };
}

Поэтому, если у меня есть два потока, вызывающих этот метод непрерывно и одновременно, один с собаками (скажем, «великий датчанин» и «собака-бык»), а другой с кошками (скажем, «персидский» и «сиамский»), я когда-нибудь получу кошек и собак в том же массиве? Или кошки и собаки никогда не будут находиться внутри одного и того же вызова метода в одно и то же время?

санки
источник
другая тема по этому вопросу: stackoverflow.com/questions/8015797/…
象 嘉 道
2
Это другой вопрос, является ли статический вызов метода потокобезопасным, а не массивами.
Сани

Ответы:

212

Этот метод на 100% безопасен для потоков, так было бы, даже если бы не было static. Проблема с безопасностью потоков возникает, когда вам нужно обмениваться данными между потоками - вы должны заботиться об атомарности, видимости и т. Д.

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

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

Томаш Нуркевич
источник
4
Правильный ответ. Переменные уровня метода реплицируются в каждом стеке выполнения потока.
Сид
технически метод должен быть встроен, а параметры будут регистры процессора. Тем не менее, ответ правильный
bestsss
43
Стек, конечно, локальн по отношению к текущему потоку, но вы можете иметь ссылки на общие объекты в этом стеке. Это не проблема в примере, поскольку строки являются неизменяемыми, но метод, который изменяет переданный параметр, может иметь проблемы с безопасностью потока, если этот переданный объект доступен из нескольких потоков.
Йорн Хорстманн
1
Как упомянул @TomaszNurkiewicz, если мы передадим ссылку на изменяемый объект, мы можем попасть в условия гонки. Верно ли это, даже если метод никак не изменяет объект? Я имею в виду, это все еще будет классифицироваться как состояние гонки, потому что объект был изменчив? А что если мы добавим ключевое слово final к параметрам?
Рафаи
Что если я передам объект класса в методе? Будет ли varaible в стеке или куче?
grep
28

Метод может быть потокобезопасным только в том случае, если он изменяет некоторое общее состояние. Является ли это статическим или нет, не имеет значения.

Конрад Гарус
источник
3
@Konrad_Garus Вопрос здесь был в том, что локальные переменные представляют собой общее состояние или нет, или был ли стек для статического метода для каждого потока или общим.
Сани
«Метод может быть потокобезопасным только тогда, когда он изменяет некоторое общее состояние». Нет, он также может быть небезопасным, если он просто обращается к общему состоянию, не изменяя его. Несинхронизированный доступ к изменяемому объекту может вызвать несогласованное состояние, если объект мутирует другим потоком, даже если другой поток правильно синхронизирован. Оба потока нуждаются в соответствующей синхронизации для обеспечения безопасности потока.
Уоррен Дью
12

Эта функция совершенно безопасна для потоков.

Если вы думаете об этом ... предположите, что случилось бы, если бы это было иначе. У каждой обычной функции были бы проблемы с потоками, если бы они не были синхронизированы, поэтому все функции API в JDK должны были бы быть синхронизированы, потому что они могли бы потенциально вызываться несколькими потоками. И поскольку в большинстве случаев приложение использует какой-то API, многопоточные приложения были бы фактически невозможны.

Это слишком нелепо, чтобы думать об этом, так что только для вас: методы не являются потокобезопасными, если есть явная причина, по которой могут возникнуть проблемы. Старайтесь всегда думать о том, что если бы в моей функции было несколько потоков, а что, если бы у вас был пошаговый отладчик, и один шаг за другим продвигал бы первый ... затем второй поток ... может быть, второй снова ... будут ли проблемы? Если вы найдете один, это не потокобезопасный.

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

И если вы действительно хотите погрузиться в это, внимательно посмотрите на ключевое слово volatile и ВСЕ его побочные эффекты. Посмотрите на классы Semaphore () и Lock () и их друзей в java.util.Concurrent. Прочитайте все документы API вокруг классов. Стоит учиться и сытно тоже.

Извините за этот слишком сложный ответ.

Даниил
источник
2
«Если вы подумаете об этом ... предположите, что произойдет, если бы это было по-другому. У каждой обычной функции были бы проблемы с потоками, если бы они не были синхронизированы, поэтому все функции API в JDK должны были бы быть синхронизированы, потому что они могли бы потенциально вызываться несколькими потоки." Хорошая точка зрения!
Сани
1

Используйте staticключевое слово с синхронизированными статическими методами, чтобы изменить статические данные, общие для потоков. С staticключевым словом все созданные потоки будут бороться за одну версию метода.

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

Clinton
источник
0

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

суровый
источник
Вопрос был в том, увидит ли когда-нибудь статический вызов метода аргументы другого вызова того же метода из другого потока. Ответ звучит "нет!" Я знал это, но хотел доказать сомнительным коллегам.
Сани
Почему этот ответ был отклонен? Дело в том, что другие ответы не привели к тому, что изменчивость может быть внутренней или внешней. Если вы возвращаете изменяемый файл, значит, вы не безопасны. Вопрос не ставит свой вопрос так узко, как предполагает комментарий; расширенный вопрос после примера кода можно было бы выразить в коде, возможно, в виде модульного теста. Но я сочувствую попыткам убедить коллег. «Они не верят ни мне, ни моему тестовому коду, ни Джошу Блоху, но, возможно, они примут ответ на SO».
Сом-Снитт