Dobieranie się do obiektów przy wielu wątkach

0
MyInnerObject mio = new MyInnerObject();
mio.setVar(0);
MyObject mo = new MyObject(mio);

new Thread(() -> {
    mo.getMyInnerObject().setVar(1);     
}).start();
Thread.sleep(1000);

assertEquals(1, mio.getVar());

Prawda czy fałsz?

2

Na 99% powinna być prawda. Ewentualnie fałsz mógłby być spowodowany tym że zmienna var nie ma modyfikatora volatile.
Nie możesz sam sprawdzić? :P

0
Aleksander32 napisał(a):

Nie możesz sam sprawdzić? :P

no mi wychodzi, że fałsz i już zgłupiałem dlatego musiałem zapytać, ech... może źle to odczytuję jak do jutra nie rozwiążę to wrzucę cały kod


a już wiem, tworzę 2 razy tego MyInnerObject xD

3

W teorii może być false bo nie ma żadnego memory fence i nie ma pewności że zewnętrzny wątek będzie widział zmiany które robi wątek wewnętrzny i tutaj wszystko zależy w dużej mierze od race condition z kolejnością wykonania. Jeśli wątek umrze zanim dojdziesz do swojej asercji to będzie true ale jak nie, to loteria. Jak chcesz coś takiego robić to używaj jakiegoś AtomicReference albo synchronizuj dostęp. To twoje MyInnerObject wygląda właśnie jak bieda-AtomicReference.

3

Tu sprawa jest jasna
wynik jest niezdefiniowany. @Shalom napisał dlaczego.

Żaden Thread.sleep( strasznie dużo) nie gwarantuje Ci, że odpalone wątki ruszą i się zakończą. A nawet jeśli tak się zdarzy, to formalnie nie widzisz rezultatów.

Brakuje wykorzystania Thread.join

0

Prawda, chyba ze 2-gi watek sie nie wyrobi w czasie ok 1s.

2

Używanie Thread.sleep() do synchronizowania wątków w testach i poza nimi to mocny code smell i proszenie się o problemy :) Może być tak, że w 50% takie usypianie jako-tako działa, w 25% uśpienie jest zbyt krótkie i coś nie działa, a w 25% jest zbyt długie i... też nie działa. Wtedy w miarę szybko zorientujesz się, że coś nie gra.

Gorzej, jak to usypianie wątku zadziała w 999999 przypadkach na milion, a w jednym coś się wywali. Bardzo ciężko będzie zreprodukować taki błąd i dojść do tego, jaka jest jego przyczyna :P

Jeśli w testach potrzebujesz zaczekać np. na wynik z innego wątku lub ogólnie masz do czynienia z asynchronicznym wykonaniem, to możesz użyć awaitility. Jeśli dodatkowo nie zależy Ci jakoś szczególnie na grzebaniu bezpośrednio w Threadach, możesz ułatwić sobie życie sięgając po ExecutorService i żonglować na poziomie zadań, a nie tworzenia i synchronizowania/łączenia wątków.

0

Mały test. Przechodzi losowo więcej jakby się mogło 'wydawać'. Main czeka tylko jedną mikrosekundę. Thread dostaje zestaw od 1 mikrosekundy wzwyż.
Z drugiej strony gdyby to była maszyna wieloprocesorowa (fizycznie wiele procesorów) to niespodziewanie na produkcji może się posypać to co do tej pory nie robiło błędów.

package pl.bv.other;

import lombok.SneakyThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;

import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

@RunWith(Parameterized.class)
public class MyObjectTest {

    @Parameter
    public boolean isDaemon;
    @Parameter(value = 1)
    public int sleepTime;

    @Parameterized.Parameters
    public static Collection<Object[]> data() {

        return Arrays.asList(new Object[][]{
                {false, 1},
                {false, 10},
                {false, 100},
                {false, 1000},
                {false, 10_000},
                {false, 100_000},
                {false, 1000_000},
                {true, 1},
                {true, 10},
                {true, 100},
                {true, 1000},
                {true, 10_000},
                {true, 100_000},
                {true, 1000_000}
        });
    }

    @SneakyThrows
    @Test
    public void shouldBeCasino() {

        final MyInnerObject myInnerObject = new MyInnerObject();
        myInnerObject.setVar(0);

        final MyObject myObject = new MyObject(myInnerObject);

        final Thread thread = new Thread(() -> {
            try {
                TimeUnit.MICROSECONDS.sleep(sleepTime);
            } catch (InterruptedException ignore) {
            }
            myObject.getMyInnerObject().setVar(123456789);
        });

        thread.setDaemon(isDaemon);
        thread.start();

        TimeUnit.MICROSECONDS.sleep(1);

        assertThat(myInnerObject.getVar(), is(123456789));
    }
}

Jak nie zapewnisz reguł (atomic, synchronized, thread safe kolekcji) to jest kasyno.

2

Jak jest niepoprawne to jest niepoprawne.
Nieważne, że teraz na jakieś maszynie prawie zawsze działa.
Wystarczy, że zmieni się wersja jdk (optymalizacje) i już może przestać działać.
Albo (zupełnie nieprawdopodobne...) ważny dostawca sprzętu przejdzie na platformę opartą o ARM i też kupa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1