Наскоро започнах да уча C# и се насочих направо към модела на паметта. C# и Java имат подобни (макар и може би не идентични) гаранции за безопасност на нишките по отношение на четене и запис на volatile
полета. Но за разлика от записите в final
полета в Java, записите в readonly
полета в C# не предоставят конкретна гаранция за безопасност на нишката. Мислейки за това как работи безопасността на нишките в C#, ме накара да се съмнявам дали има някакво реално предимство полетата final
да се държат по начина, по който се държат в Java.
Научих за предполагаемата важност на final
преди три години. Зададох този въпрос и получих подробен отговор, който приех. Но сега мисля, че е грешно или поне без значение. Все още смятам, че полетата трябва да бъдат final
, когато е възможно, просто не поради причините, за които се смята, че обикновено се смята.
Стойността на поле final
е гарантирано видима за всяка друга нишка, след като конструкторът се върне. Но препратката към самия обект трябва да бъде публикувана по безопасен начин. Ако препратката е публикувана безопасно, тогава гаранцията за видимост на final
става излишна.
Обмислих възможността това да има нещо общо с public static
полета. Но логично зареждането на клас трябва да синхронизира инициализацията на класа. Синхронизирането прави безопасността на нишката на final
излишна.
Така че излагам еретичната идея, че единствената истинска стойност на final
е да направи неизменността самодокументираща се и самоналагаща се. На практика частните неокончателни полета (и по-специално елементите на масив) са напълно безопасни за нишки, стига да не бъдат модифицирани след връщането на конструктора.
Греша ли?
Редактиране: Перифразирайки раздел 3.5 от Java Concurrency на практика,
Две неща могат да се объркат с неправилно публикувани обекти. Други нишки могат да видят остаряла стойност за препратката и по този начин да видят нулева препратка или друга по-стара стойност, въпреки че е зададена стойност. Но много по-лошо е, че други нишки могат да видят актуална стойност за референтната стойност, но остарели стойности за състоянието на обекта.
Разбирам как полетата final
решават втория проблем, но не и първия проблем. Най-високо гласуваният отговор досега твърди, че първият проблем не е проблем.
Редактиране 2: Този въпрос възниква от объркване на терминологията.
Подобно на задаващия подобен въпрос, винаги съм разбирал термина „безопасно публикуване“ като значение, че вътрешното състояние на обекта и препратката към самия обект са гарантирано видими за други нишки. В полза на това определение Effective Java цитира Goetz06, 3.5.3 при дефинирането на „безопасна публикация“ като (курсивът е добавен)
Прехвърляне на такава препратка към обект от една нишка към друга
Също така в полза на това определение, имайте предвид, че разделът на Java Concurrency in Practice, перифразиран по-горе, се отнася до потенциално остарели препратки като „неправилно публикувани“.
Както и да го наречете, не мислех, че небезопасното публикуване на препратка към неизменен обект може да бъде полезно. Но според този отговор може. (Примерът, даден там, е примитивна стойност, но същият принцип може да се прилага за референтни стойности.)