Чи безпечні несинхронізовані статичні методи, якщо вони не змінюють змінні статичного класу?


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, якщо ми передамо посилання на змінний об'єкт, ми можемо потрапити в умови перегонів. Чи справедливо це, навіть якщо метод жодним чином не змінює об'єкт? Я маю на увазі, чи все одно це буде класифіковано як стан перегонів, оскільки об'єкт був змінним? А що, якщо додати параметри остаточне ключове слово?
Рафай

Що робити, якщо я передаю об’єкт класу в методі? Чи буде змінний у стеці чи купі?
греп

28

Метод може бути небезпечним для потоків, лише коли він змінює деякий загальний стан. Будь статичний чи ні, не має значення.


3
@Konrad_Garus Питання тут полягало у тому, чи локальні змінні складають спільний стан, чи ні, чи є стек для статичного методу на потік чи спільний.
Sled

"Метод може бути небезпечним для потоків, лише коли він змінює деякий загальний стан." Ні, він також може бути небезпечним для потоку, якщо він просто отримує доступ до загального стану, не змінюючи його. Несинхронізований доступ до об'єкта, що змінюється, може отримати доступ до непослідовного стану, якщо об'єкт мутується іншим потоком, навіть якщо інший потік належним чином синхронізований. Обидва потоки потребують відповідної синхронізації для забезпечення безпеки потоку.
Warren Dew

12

Ця функція ідеально захищена від потоку.

Якщо ви подумаєте над цим ... припустимо, що було б, якби це було інакше. Кожна звичайна функція мала б проблеми з ниткою, якщо їх не синхронізувати, тому всі функції API в JDK повинні були бути синхронізовані, оскільки вони потенційно можуть викликатися декількома потоками. А оскільки більшість часу програма використовує деякі API, багатопотокові програми фактично були б неможливі.

Це надто смішно, щоб думати про це, тож тільки для вас: методи не є безпечними, якщо є чітка причина, чому можуть виникнути проблеми. Постарайтеся завжди думати про те, що якби у моїй функції було декілька потоків, і що, якби у вас був крок-налагоджувач і чи буде один крок за іншим просуватися перший ... потім другий потік ... можливо, другий знову ... чи були б проблеми? Якщо ви знайдете його, його не захистіть від потоку.

Будь ласка, майте на увазі, що більшість класів колекції Java 1.5 не є безпечними для потоків, крім тих, де зазначено, як ConcurrentHashMap.

І якщо ви дійсно хочете зануритися в це, уважно подивіться на мінливе ключове слово та ВСІ його побічні ефекти. Погляньте на класи Semaphore () та Lock () та їхніх друзів у java.util.Concurrent. Прочитайте всі документи API навколо класів. Варто вчитися і задовольняти теж.

Вибачте за цю надмірно детальну відповідь.


2
"Якщо ви подумаєте про це ... припустимо, що трапилося б, якби це було іншим. Кожна звичайна функція мала б проблеми з ниткою, якщо не синхронізована, тому всі функції API в JDK повинні були бути синхронізовані, тому що вони потенційно можуть бути викликані декількома нитки. " Гарна думка!
Sled

1

Використовуйте staticключове слово із синхронізованими статичними методами, щоб змінити статичні дані, що поділяються між потоками. За допомогою staticключового слова всі створені нитки змагатимуться за одну версію методу.

Використання volatileключового слова разом із методами синхронізованого примірника гарантуватиме, що кожен потік має власну копію спільних даних і жодне читання / запис не просочиться між потоками.


0

Строкові об'єкти, які не змінюються, є ще однією причиною вищезахисного сценарію. Замість цього, якщо використовуються змінні об’єкти (скажімо, makeMutableArray ..), то, безумовно, безпека потоків порушиться.


Питання полягало в тому, якщо статичний виклик методу коли-небудь побачить аргументи іншого виклику цього ж методу з іншого потоку. Відповідь - гучне "ні!". Це я знав, але хотів довести до сумнівних колег.
Санки

Чому ця відповідь була заперечена? Суть, яку не сприяють інші відповіді, полягає в тому, що змінність може бути пов’язаною або зв'язаною. Якщо ви повернете змінений, то ви не безпечні. Питання не ставить його питання настільки вузько, як підказує коментар; розширене питання після зразка коду могло бути виражене в коді, можливо, як одиничний тест. Але я співчуваю намаганням переконати колег. "Вони не вірять мені, чи моєму тестовому коду, або Джошу Блоху, але, можливо, вони приймуть відповідь на ТАК".
som-snytt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.