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


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ключевого слова вместе с синхронизированными методами экземпляров гарантирует, что каждый поток имеет свою собственную копию общих данных и никакие операции чтения / записи не будут просачиваться между потоками.


0

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


Вопрос был в том, увидит ли когда-нибудь статический вызов метода аргументы другого вызова того же метода из другого потока. Ответ звучит "нет!" Я знал это, но хотел доказать сомнительным коллегам.
— Сани

Почему этот ответ был отклонен? Дело в том, что другие ответы не привели к тому, что изменчивость может быть внутренней или внешней. Если вы возвращаете изменяемый файл, значит, вы не безопасны. Вопрос не ставит свой вопрос так узко, как предполагает комментарий; расширенный вопрос после примера кода можно было бы выразить в коде, возможно, в виде модульного теста. Но я сочувствую попыткам убедить коллег. «Они не верят ни мне, ни моему тестовому коду, ни Джошу Блоху, но, возможно, они примут ответ на SO».
— Сом-Снитт
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.