Java: Mám neprázdný Set, ale nemůžu z něj dostat položky

Takže nevím, zda je root ideální server pro tento dotaz, ale zkusím to  :)

Mám v javě

Kód: [Vybrat]
package cz.exambuilder.entity.domain.calendar;
(...)
public class Calendar {
    protected static final Set<Image> imagesQueue = new LinkedHashSet<>();
    (...)
}

a ten set imagesQueue je neprázdný:

Kód: [Vybrat]
imagesQueue.isEmpty(); // dává false
imagesQueue.size(); // dává 1

Problém je, že položky toho setu nemůžu získat:

Kód: [Vybrat]
imagesQueue.iterator().hasNext(); // dává false
imagesQueue.iterator().next(); //  hodí java.util.NoSuchElementException.

Podstatné, že k tomuto stavu dojde až po cca měsíčním běhu aplikace, zprvu to funguje normálně.

Čichal bych tady nějaký problém v implementaci equals(), hashCode() nebo compareTo(), ale na nic jsem nepřišel, používám výchozí implementace z frameworku. Možná vstoupí do hry nějaký JIT nebo přesun instancí do jiné oblasti paměti a změní se hashcode nebo něco podobného? Na co se mám zaměřit? Uvažoval jsem ještě nad tím, zda se to do tohoto stavu nemůže dostat špatně ošetřeným konkurenčním přístupem, ale nedokážu si to představit.

Pozitivní je, že mám k dispozici testovací prostředí, kde to ještě funguje i prostředí, kde to už nefunguje - do obou se můžu se připojit debuggerem nebo z vývojové konzole umožňující spouštět interaktivně kód.

Na co se mám zaměřit? Kdy k něčemu takovému může v principu vůbec dojít?

Používám java version "1.8.0_161", apache-tomcat-8.5.5, Debian 4.8.4-1 a Brightspot ve verzi v3.2.7178-2110f8

Aktuální testovací kód:

Kód: [Vybrat]
import cz.exambuilder.entity.domain.calendar.Calendar;
import java.util.stream.*;
import java.lang.reflect.*;
import org.slf4j.*;

public class Code {
    public static Object main() throws Throwable {

        Logger log = LoggerFactory.getLogger(Code.class);

        // tady získám tu privátní instanci imagesQueue, abych se na ní mohl podívat:

        Calendar calendar = Query.from(Calendar.class).first();
        Field field = Calendar.class.getDeclaredField("imagesQueue");
        field.setAccessible(true);
        Set<Image> imagesQueue = (Set<Image>) field.get(calendar);

        // tady zkouším, co imagesQueue obsahuje:

        log.info("*** imagesQueue.isEmpty(): " + imagesQueue.isEmpty()); // false
        log.info("*** imagesQueue.size(): " + imagesQueue.size()); // 1

        log.info("*** imagesQueue.stream(): " + imagesQueue.stream().map(i -> String.format("%s (%s)", i.getTitle(), i.getId())).collect(Collectors.toList())); // prázdná kolekce
        log.info("*** imagesQueue.iterator().hasNext(): " + imagesQueue.iterator().hasNext()); // false

        try {
           log.info("*** imagesQueue.iterator().next(): " + imagesQueue.iterator().next()); // NoSuchElementException
        } catch(Exception e){
            log.info("*** exception: " + e);
        }
       
        return "SEE LOGS";

    }
}

Dík za nápady!


Kit

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #1 kdy: 12. 04. 2018, 18:13:46 »
Kód: [Vybrat]
Iterator it = imagesQueue.iterator();
if (it.hasNext()) {
    System.out.println(it.next());
}

Řetězení se občas nevyplácí.

Phi

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #2 kdy: 12. 04. 2018, 18:26:40 »
Nevím. Jsou ty Image mutable nebo volatile?

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #3 kdy: 12. 04. 2018, 18:29:03 »
Jak vis, ze ti do te fronty mezi tim nehrabe nekdo jiny?

Kit

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #4 kdy: 12. 04. 2018, 18:43:55 »
Kód: [Vybrat]
imagesQueue.iterator().hasNext(); // dává false
imagesQueue.iterator().next(); //  hodí java.util.NoSuchElementException.

Na těchto dvou řádcích vytváříš dva různé objekty třídy Iterate, s každým manipuluješ zvlášť. Pokud máš podobná zvěrstva i jinde v kódu, tak se není čemu divit, že ti občas nějaký souběh něco poničí.


Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #5 kdy: 12. 04. 2018, 19:19:26 »
Kód: [Vybrat]
Iterator it = imagesQueue.iterator();
if (it.hasNext()) {
    System.out.println(it.next());
}

Řetězení se občas nevyplácí.
Kód: [Vybrat]
imagesQueue.iterator().hasNext(); // dává false
imagesQueue.iterator().next(); //  hodí java.util.NoSuchElementException.

Na těchto dvou řádcích vytváříš dva různé objekty třídy Iterate, s každým manipuluješ zvlášť. Pokud máš podobná zvěrstva i jinde v kódu, tak se není čemu divit, že ti občas nějaký souběh něco poničí.

Samozřejmě že nedělám, každopádně meritem problému je že isEmpty je false a iterator().hasNext() je taky false. A jak k tomu problému jednou dojde, tak je už trvalý.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #6 kdy: 12. 04. 2018, 19:33:52 »
Jak vis, ze ti do te fronty mezi tim nehrabe nekdo jiny?

Pokud myslíš počas toho testu, tak například tak, že se připojím debuggerem, zastavím na breakpointu, který pauzne celou jvm a pak si v vyhodnotím příkazy imagesQueue.isEmpty(); imagesQueue.iterator().hasNext(); To by mělo být jisté, že do toho nikdo nehrabe, ne?

Pokud myslíš po celou dobu běhu aplikace, pak tam určitě hrabe víc vláken, modifikace se dějí v několika metodách třídy Calendar a ty metody jsou synchronizované, takže by se měly synchronizovat přes tu instanci Calendar. Pak můžu i v testu dát synchronizovaný blok přes tu instanci Calendar a mělo by to být taky košer, nebo ne?

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #7 kdy: 12. 04. 2018, 19:46:30 »
Ten main synchronizovany nevidim. Zda ti debugger zastavi vsechny thready neni jiste. To se musis ujistit v dokumentaci sveho debuggeru.

Kit

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #8 kdy: 12. 04. 2018, 19:47:30 »
Kód: [Vybrat]
imagesQueue.iterator().hasNext(); // dává false
imagesQueue.iterator().next(); //  hodí java.util.NoSuchElementException.

Na těchto dvou řádcích vytváříš dva různé objekty třídy Iterate, s každým manipuluješ zvlášť. Pokud máš podobná zvěrstva i jinde v kódu, tak se není čemu divit, že ti občas nějaký souběh něco poničí.

Samozřejmě že nedělám, každopádně meritem problému je že isEmpty je false a iterator().hasNext() je taky false. A jak k tomu problému jednou dojde, tak je už trvalý.

Děláš. Každé volání imagesQueue.iterator() vytváří novou instanci třídy Iterator. Takže je to i lenošnější, než by mohlo být.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #9 kdy: 12. 04. 2018, 19:51:07 »
Kód: [Vybrat]
imagesQueue.iterator().hasNext(); // dává false
imagesQueue.iterator().next(); //  hodí java.util.NoSuchElementException.

Na těchto dvou řádcích vytváříš dva různé objekty třídy Iterate, s každým manipuluješ zvlášť. Pokud máš podobná zvěrstva i jinde v kódu, tak se není čemu divit, že ti občas nějaký souběh něco poničí.

Samozřejmě že nedělám, každopádně meritem problému je že isEmpty je false a iterator().hasNext() je taky false. A jak k tomu problému jednou dojde, tak je už trvalý.

Děláš. Každé volání imagesQueue.iterator() vytváří novou instanci třídy Iterator. Takže je to i lenošnější, než by mohlo být.

To tady fakt neni jadro problemu. Je to spatny styl, ale i kdyby tam to druhe volani nebylo, tak je tam porad problem.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #10 kdy: 12. 04. 2018, 19:51:35 »
Kód: [Vybrat]
imagesQueue.iterator().hasNext(); // dává false
imagesQueue.iterator().next(); //  hodí java.util.NoSuchElementException.

Na těchto dvou řádcích vytváříš dva různé objekty třídy Iterate, s každým manipuluješ zvlášť. Pokud máš podobná zvěrstva i jinde v kódu, tak se není čemu divit, že ti občas nějaký souběh něco poničí.

Samozřejmě že nedělám, každopádně meritem problému je že isEmpty je false a iterator().hasNext() je taky false. A jak k tomu problému jednou dojde, tak je už trvalý.

Děláš. Každé volání imagesQueue.iterator() vytváří novou instanci třídy Iterator. Takže je to i lenošnější, než by mohlo být.

Ano, Kite, to já vím. Snažil jsem se jen sdělit, že tento druh zvěrstev jinde v kódu nemám. To spíš blbne ta synchronizace.
Kite, tohle já vím. Snažil jsem se vysvětlit, že tento druh zvěrstev jinde v kódu nemám. To tam možná spíš blbne ta synchronizace.
« Poslední změna: 12. 04. 2018, 19:53:34 od Ondrej Nemecek »

Kit

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #11 kdy: 12. 04. 2018, 19:52:44 »
Co takhle to udělet bez iterátoru? Stejně je tam zbytečný.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #12 kdy: 12. 04. 2018, 20:00:28 »
Ten main synchronizovany nevidim. Zda ti debugger zastavi vsechny thready neni jiste. To se musis ujistit v dokumentaci sveho debuggeru.

Zastaví všechno, mám to nastavený u breakpointu. Ta imagesQueue se mění v metodách Calendar, ty metody jsou synchronizované, takže se synchronizují přes instanci Calendar. Ta by měla být jen jedna, protože Calendar je singleton. Ale napadá mě, že není jisté, co tím singletonem framework Brightspot má na mysli, protože to, že je Calendar singleton zajišťuje nějak interně framework. Možná ale nění   zajištěno, že bude vše doslova jediná instance. To by mohl být zdroj problému. Tahle varianta se mi zdá čím dál tím pravděpodobnější.

Aktuální podoba testu:

Kód: [Vybrat]
import cz.exambuilder.entity.domain.calendar.Calendar;
import java.util.stream.*;
import java.lang.reflect.*;
import org.slf4j.*;

public class Code {
    public static Object main() throws Throwable {

        Calendar calendar = Query.from(Calendar.class).first();

        synchronized(calendar){
            Logger log = LoggerFactory.getLogger(Code.class);
   
            // tady získám tu privátní instanci imagesQueue, abych se na ní mohl podívat:
   
            Field field = Calendar.class.getDeclaredField("imagesQueue");
            field.setAccessible(true);
            Set<Image> imagesQueue = (Set<Image>) field.get(calendar);
            // imagesQueue.clear();
            Iterator imagesIterator = imagesQueue.iterator();
   
            // tady zkouším, co imagesQueue obsahuje:
   
            log.info("*** calendar.getPresenationImages(1)" + calendar.getPresenationImages(1).size());
            log.info("*** imagesQueue.isEmpty(): " + imagesQueue.isEmpty()); // false
            log.info("*** imagesQueue.size(): " + imagesQueue.size()); // 1
            log.info("*** imagesQueue.stream(): " + imagesQueue.stream().map(i -> String.format("%s (%s)", i.getTitle(), i.getId())).collect(Collectors.toList())); // prázdná kolekce
            log.info("*** imagesIterator.hasNext(): " + imagesIterator.hasNext()); // false
        }
       
        return "SEE LOGS";

    }
}

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #13 kdy: 12. 04. 2018, 20:10:13 »
Nevím. Jsou ty Image mutable nebo volatile?

Mutable - Image jsou mutable (můžou se měnit).

Volatile - ta imagesQueue není volatile a co se týče referencí na jednotlivé Image si nejsem jist (položky imagesQueue získává framework).

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #14 kdy: 12. 04. 2018, 20:42:44 »
Ale špatná synchronizace přístupu při změně té fronty by neměla vést k trvalé nepoužitelnosti té fronty, ne? Takže jeden problém je, zda je synchronizace v pořádku a druhý problém je, jakto že se ta fronta dostane do nevalidního stavu (není prázdná ale nemůžu dostat její prvky a to ani při pozastavených vláknech jvm).