Бяспека патоку канчатковага поля

Скажам, у мяне ёсць JavaBean User , які абнаўляецца з іншага патоку, як гэта:

public class A {

    private final User user;

    public A(User user) {
        this.user = user;
    }

    public void aMethod() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                ...a long running task..
                user.setSomething(something);
            }

        });
        t.start();
        t.join();
    }

    public void anotherMethod() {
        GUIHandler.showOnGuiSomehow(user);
    }
}

Ці з'яўляецца гэты код потокобезопасными? Я маю на ўвазе, калі паток, які стварыў асобнік і называецца A.aMethod чытае прыстасаваныя поля, яна бачыць карыстач у свежым стане? Як гэта зрабіць у адпаведнай ніткі бяспечным спосабам?

Звярніце ўвагу, што я не магу змяніць клас карыстальнікаў, і я не ведаю, калі гэта струменева сам.

5

10 адказы

маркіроўка поле Канчатковы проста азначае, што спасылка не можа быць зменены. Гэта нічога не значыць, пра ніткі safity класа User . Калі метады гэтага класа, доступ да палёў сінхранізаваныя (або выкарыстаць іншы метад сінхранізацыі) з'яўляецца струменева. У адваротным выпадку гэта не так.

4
дададзена
Глядзіце пытанне абнаўлення калі ласка.
дададзена аўтар Behnil, крыніца

маркіроўка поле Канчатковы проста азначае, што спасылка не можа быць зменены. Гэта нічога не значыць, пра ніткі safity класа User . Калі метады гэтага класа, доступ да палёў сінхранізаваныя (або выкарыстаць іншы метад сінхранізацыі) з'яўляецца струменева. У адваротным выпадку гэта не так.

4
дададзена
Глядзіце пытанне абнаўлення калі ласка.
дададзена аўтар Behnil, крыніца
<�Р> Ці з'яўляецца гэты код потокобезопасными? ... яна бачыць карыстач у свежым стане?

Ня асабліва - той факт, што карыстальніка з'яўляецца канчатковым у ваш код робіць практычна ніякай розніцы ў Нітачны, акрамя таго, што ён не можа быць заменены.

Біт, які павінен змяніць гэта зменная асобніка, усталёўваецца setSomething . Ён павінен быць пазначаны як лятучага .

class User {
 //Marked `volatile` to ensure all writes are visible to other threads.
  volatile String something;

  public void setSomething(String something) {
    this.something = something;
  }

}

Калі ж (як вы прапануеце) вы не маеце доступу да Карыстальнік клас, вы павінны затым выканаць сінхранізацыю, якая стварае бар'ер памяці. У сваёй найпростай форме вы маглі б акружыць доступ да карыстальніка з сінхронным доступу.

synchronized (user) {
  user.setSomething(something);
}

Паведамленні: - Аказваецца (гл тут ), што гэта на самай справе можна зрабіць так:

volatile int barrier = 0;
...
user.setSomething(something);
// Forces **all** cached variable to be flushed.
barrier += 1;
4
дададзена
Скажам, я не магу змяніць клас карыстальніка якім-небудзь чынам.
дададзена аўтар Behnil, крыніца
@Behnil - Адказ падоўжаны.
дададзена аўтар OldCurmudgeon, крыніца

Так, гэта бяспечна. глядзець

Java Language Specification (Java 8) Chapter 17.4.4:

<�Р> канчатковае рашэнне ў струмень T1-сінхранізуе з любым дзеяннем ў іншым струмені Т2, які выяўляе, што Т1 спыняецца. </Р>      <�Р> Т2 можа зрабіць гэта шляхам выкліку T1.isAlive() або T1.join (). </Р>

Змесціце гэта разам з 17.4.5 , Здараецца, перад замовай :

<�Р> Два дзеянні могуць быць упарадкавана бывае, да адносін. Калі адно дзеянне адбываецца, перш, чым іншы, то першы бачная і загадаў перад другім. [..] Калі дзеянне х сінхранізуе-са наступным дзеяннем у, то мы таксама маем Hb (х, у). </Р>

Таму пасля выкліку t.join (); у вашым кодзе, вы ўбачыце абноўленыя змены. Бо «паток, які стварыў асобнік і называецца A.aMethod» можа дашчэнту счытваць значэнне пасля выкліку aMethod і перад t.join называецца (таму што ён заняты з метадам aMethod ), гэта бяспечна.

0
дададзена

Так, гэта бяспечна. глядзець

Java Language Specification (Java 8) Chapter 17.4.4:

<�Р> канчатковае рашэнне ў струмень T1-сінхранізуе з любым дзеяннем ў іншым струмені Т2, які выяўляе, што Т1 спыняецца. </Р>      <�Р> Т2 можа зрабіць гэта шляхам выкліку T1.isAlive() або T1.join (). </Р>

Змесціце гэта разам з 17.4.5 , Здараецца, перад замовай :

<�Р> Два дзеянні могуць быць упарадкавана бывае, да адносін. Калі адно дзеянне адбываецца, перш, чым іншы, то першы бачная і загадаў перад другім. [..] Калі дзеянне х сінхранізуе-са наступным дзеяннем у, то мы таксама маем Hb (х, у). </Р>

Таму пасля выкліку t.join (); у вашым кодзе, вы ўбачыце абноўленыя змены. Бо «паток, які стварыў асобнік і называецца A.aMethod» можа дашчэнту счытваць значэнне пасля выкліку aMethod і перад t.join называецца (таму што ён заняты з метадам aMethod ), гэта бяспечна.

0
дададзена

Што тычыцца наразанне разьбы, канчатковыя поля проста гарантавана быць паслядоўным у выпадку канструктар ўцёкі , так як JSR-133 аб механізме памяці Бар'ер:

<�Р> Значэнні для канчатковых палёў аб'екта пералічваюцца ўстаноўлены ў канструктару.   Мяркуючы, што аб'ект будуецца "правільна", як толькі аб'ект   пабудаваныя, значэння прысвойваюцца канчатковым палёў у   Канструктар будзе бачны ўсім іншым патокаў без   сінхранізацыі. Акрамя таго, бачныя значэння для любога іншага аб'екта   або масіў спасылаецца гэтыя палі канчатковых будзе па меншай меры,   ўдакладнены як канчатковых палёў. Што гэта значыць для аб'екта быць   правільна пабудаваная? Гэта проста азначае, што няма ніякай спасылкі на аб'ект   будуецца дазволена «бегчы» падчас будаўніцтва. (Гл   Бяспечныя будаўнічыя метады для прыкладаў.) Іншымі словамі, не   размясціць спасылку на аб'ект будуецца ў любым месцы, дзе   іншы паток можа быць у стане бачыць яго; не прызначаць яго на статычны   полі, не зарэгістраваць яго ў якасці слухача з любым іншым аб'ектам, і так   на. Гэтыя задачы павінны быць зробленыя пасля завяршэння канструктара, а не ў   канструктар.

Тым не менш, <�моцны> нічога не гарантуе аўтаматычную бяспеку патокаў аб любых канчатковых палях у жыцці Пакінуты аб'екта (г.зн. пасля абкручванні выканання канструктара класа,). . Сапраўды, нязменнасць на Java з'яўляецца чыста няправільна:

<�Р> Зараз, у прастамоўі, нязменнасць азначае, што «не мяняецца».   Нязменнасць не азначае, што «не мяняецца» ў Java. Гэта азначае, што «ёсць   транзітыўнасць дасягальна з канчатковага поля, не змяніліся, паколькі   апошняе поле было ўстаноўлена, і спасылка на аб'ект, якое змяшчае   Апошняе поле не абмінуў канструктар ».
0
дададзена
Я не думаю, што дадзенае вызначэнне нязменнасці цалкам мае рацыю. Калі спасылка на аб'ект была захавана ў <�кода канчатковага /> поле, але некаторыя іншыя спасылкі на гэтае месца таксама існуе, я не думаю, што аб'ект будзе «нязменным» з пункту гледжання якіх-небудзь спасылкі <�я>, які не быў прачытаны або скапіяваны з </канчатковага кода> поле .
дададзена аўтар supercat, крыніца

Што тычыцца наразанне разьбы, канчатковыя поля проста гарантавана быць паслядоўным у выпадку канструктар ўцёкі , так як JSR-133 аб механізме памяці Бар'ер:

<�Р> Значэнні для канчатковых палёў аб'екта пералічваюцца ўстаноўлены ў канструктару.   Мяркуючы, што аб'ект будуецца "правільна", як толькі аб'ект   пабудаваныя, значэння прысвойваюцца канчатковым палёў у   Канструктар будзе бачны ўсім іншым патокаў без   сінхранізацыі. Акрамя таго, бачныя значэння для любога іншага аб'екта   або масіў спасылаецца гэтыя палі канчатковых будзе па меншай меры,   ўдакладнены як канчатковых палёў. Што гэта значыць для аб'екта быць   правільна пабудаваная? Гэта проста азначае, што няма ніякай спасылкі на аб'ект   будуецца дазволена «бегчы» падчас будаўніцтва. (Гл   Бяспечныя будаўнічыя метады для прыкладаў.) Іншымі словамі, не   размясціць спасылку на аб'ект будуецца ў любым месцы, дзе   іншы паток можа быць у стане бачыць яго; не прызначаць яго на статычны   полі, не зарэгістраваць яго ў якасці слухача з любым іншым аб'ектам, і так   на. Гэтыя задачы павінны быць зробленыя пасля завяршэння канструктара, а не ў   канструктар.

Тым не менш, <�моцны> нічога не гарантуе аўтаматычную бяспеку патокаў аб любых канчатковых палях у жыцці Пакінуты аб'екта (г.зн. пасля абкручванні выканання канструктара класа,). . Сапраўды, нязменнасць на Java з'яўляецца чыста няправільна:

<�Р> Зараз, у прастамоўі, нязменнасць азначае, што «не мяняецца».   Нязменнасць не азначае, што «не мяняецца» ў Java. Гэта азначае, што «ёсць   транзітыўнасць дасягальна з канчатковага поля, не змяніліся, паколькі   апошняе поле было ўстаноўлена, і спасылка на аб'ект, якое змяшчае   Апошняе поле не абмінуў канструктар ».
0
дададзена
Я не думаю, што дадзенае вызначэнне нязменнасці цалкам мае рацыю. Калі спасылка на аб'ект была захавана ў <�кода канчатковага /> поле, але некаторыя іншыя спасылкі на гэтае месца таксама існуе, я не думаю, што аб'ект будзе «нязменным» з пункту гледжання якіх-небудзь спасылкі <�я>, які не быў прачытаны або скапіяваны з </канчатковага кода> поле .
дададзена аўтар supercat, крыніца
<�Р> Звярніце ўвагу, што я не магу змяніць клас карыстальнікаў, і я не ведаю, калі гэта струменева сам.

Вы павінны сінхранізаваць доступ пры звароце да аб'екта карыстальніка. Напрыклад, вы можаце выкарыстоўваць аб'ект карыстальніка для сінхранізацыі, так што проста абгарнуць кожны доступ да аб'екта карыстальніка нешта накшталт:

synchronized(user) {
 //access some method of the user object
}

Гэта прадугледжвае, што аб'ект карыстальніка доступ толькі ў вашых патокаў асінхронна. Акрамя таго, трымаць сінхранізаваныя блокі кароткімі.

Акрамя таго, можна пабудаваць струменева-абалонку вакол аб'екта карыстальніка. Я хацеў бы прапанаваць, што калі ў вас ёсць шмат розных выклікаў, код становіцца чысцей і лепш чытаць менавіта так.

поспехаў!

0
дададзена
<�Р> Звярніце ўвагу, што я не магу змяніць клас карыстальнікаў, і я не ведаю, калі гэта струменева сам.

Вы павінны сінхранізаваць доступ пры звароце да аб'екта карыстальніка. Напрыклад, вы можаце выкарыстоўваць аб'ект карыстальніка для сінхранізацыі, так што проста абгарнуць кожны доступ да аб'екта карыстальніка нешта накшталт:

synchronized(user) {
 //access some method of the user object
}

Гэта прадугледжвае, што аб'ект карыстальніка доступ толькі ў вашых патокаў асінхронна. Акрамя таго, трымаць сінхранізаваныя блокі кароткімі.

Акрамя таго, можна пабудаваць струменева-абалонку вакол аб'екта карыстальніка. Я хацеў бы прапанаваць, што калі ў вас ёсць шмат розных выклікаў, код становіцца чысцей і лепш чытаць менавіта так.

поспехаў!

0
дададзена

Канчатковы толькі робіць спасылкі паўторна ня прызначаныя, але калі спасылка паказвае на змененым класа, вы можаце змяніць стан ўнутры гэтага аб'екта, які выклікае THEAD небяспечныя.

Ваш код толькі струменева калі клас карыстальніка нязменны, г.зн. усе ўласцівасці карыстальніка не могуць быць зменены за межамі аб'екта, усе спасылкі ў пункце класа да іншага нязменнага класу.

Калі гэта не так, то вы павінны правільна сінхранізаваць свае метады, каб зрабіць яго струменева.

0
дададзена