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

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #45 kdy: 13. 04. 2018, 15:44:43 »
Pokud mate synchronizovane oprace nad Set<...> tak to nestaci pokud neco co je v te Set pustite mimo synchronized sekci a pak to tam menite. Musite zajistit, ze se to bud nebude menit - nejlepsi, nebo ze veskere zmeny budou opet synchronizovane.
Pokud ty změny neovlivní hashCode() a equals(), tak je tomu Setu úplně jedno, že se ten objekt mění mimo kód synchronizovaný pro tu práci se Setem. Ostatně, ten Set ani nic jiného než referenci na ten objekt a jeho hash kód nezná. něco jiného je, že je divné v nesynchronizovaném kódu měnit sdílené objekty, pak můžete vidět v každém vlákně jiný stav toho objektu – ale to není problém toho Setu. Ale ty změny klidně mohou být synchronizovány jinak, nezávisle na tom Setu (naopak je to tak správně, synchronizovat se má vždy co nejmenší blok kódu a zamykat co nejméně věcí, a nechcete zbytečně zamykat Set, když plánujete jenom změnit field jedné položky v tom Setu).

A naopak, pokud změny v tom objektu změní hashCode() nebo i jen equals(), pak to chování toho Setu rozbije i v jednovláknovém kódu, se synchronizací to nijak nesouvisí.

Nejjednodussi pravidlo je, ze cokoli je sdilene je immutable a ma to builder.
A nejjednodušší builder je konstruktor a immutable zajistí final fieldy. Pak vám to pohlídá i kompilátor, že ten objekt nejde změnit ani zevnitř.


Zdenek Henek nereg.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #46 kdy: 13. 04. 2018, 16:00:52 »
Pokud mate synchronizovane oprace nad Set<...> tak to nestaci pokud neco co je v te Set pustite mimo synchronized sekci a pak to tam menite. Musite zajistit, ze se to bud nebude menit - nejlepsi, nebo ze veskere zmeny budou opet synchronizovane.
Pokud ty změny neovlivní hashCode() a equals(), tak je tomu Setu úplně jedno, že se ten objekt mění mimo kód synchronizovaný pro tu práci se Setem. Ostatně, ten Set ani nic jiného než referenci na ten objekt a jeho hash kód nezná. něco jiného je, že je divné v nesynchronizovaném kódu měnit sdílené objekty, pak můžete vidět v každém vlákně jiný stav toho objektu – ale to není problém toho Setu. Ale ty změny klidně mohou být synchronizovány jinak, nezávisle na tom Setu (naopak je to tak správně, synchronizovat se má vždy co nejmenší blok kódu a zamykat co nejméně věcí, a nechcete zbytečně zamykat Set, když plánujete jenom změnit field jedné položky v tom Setu).

A naopak, pokud změny v tom objektu změní hashCode() nebo i jen equals(), pak to chování toho Setu rozbije i v jednovláknovém kódu, se synchronizací to nijak nesouvisí.

Nejjednodussi pravidlo je, ze cokoli je sdilene je immutable a ma to builder.
A nejjednodušší builder je konstruktor a immutable zajistí final fieldy. Pak vám to pohlídá i kompilátor, že ten objekt nejde změnit ani zevnitř.

Jeste je tu jedna moznost, ze pokud nejsou zmeny synchorinzovane, tak muze videt treba jen cast tech zmen.

Mate pravdu, ze nemenit nic co by zmenilo hashCode a equals je zaklad. Predpokladal jsem, ze toto neni ten problem, protoze to ze zacatku fungovalo a nahodne se to rozbiji, ale muze to byt i tim, ze kod, ktery meni vnitrni stav objektu byl pridan pozdeji, nebo nebyl pouzivany. Tim ze mame vse immutable, tak me toto ani nenapadlo. Jeste obcas mame problem s tim, ze objekt ma v sobe dalsi objekt a ten nema implementovany hashcode nebo equals. To je pak o nervy toto najit. Nenajde to ani Sonar, PMD, FindBugs ... a zda se, ze to nikomu nevadi, asi v kolekcich pouzivaji jen String..


Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #47 kdy: 13. 04. 2018, 16:33:17 »
Mate pravdu, ze nemenit nic co by zmenilo hashCode a equals je zaklad. Predpokladal jsem, ze toto neni ten problem, protoze to ze zacatku fungovalo a nahodne se to rozbiji, ale muze to byt i tim, ze kod, ktery meni vnitrni stav objektu byl pridan pozdeji, nebo nebyl pouzivany.
tady ale nebyl problém s prvky té kolekce, ale s tou kolekcí jako takovou.

Ale vzhledem k tomu, že LinkedhashSet je implementováno pomocí HashMap, a HashMap má field int size a metody

Kód: [Vybrat]
public int size() {
  return size;
}

public boolean isEmpty() {
 return size == 0;
}

je jasné, že si HashMapa (logicky) udržuje počet položek nezávisle na skutečném obsahu mapy. A tudíž je zřejmé, že se při nesynchronizovaném přístupu může rozejít stav „počítadla“ size se skutečným stavem mapy. Iterátor pak už samozřejmě musí jít do samotné mapy, kde zjistí, že tam ve skutečnosti nic není. Taky mohl Ondřej  skončit ve stavu, že by mu size() vracelo zápornou hodnotu… isEmpty() by bylo pořád false, a iterátor by klidně mohl nějaké hodnoty vracet.

Mně teda ale hlavně překvapuje, pokud tam Ondřej ty iterátory normálně používá, že to nikdy nevypadlo na ConcurrentModificationException. Že je to jenom taková nouzová pojistka, která ve vícevláknovém prostředí nemusí fungovat správně, je jasné, ale že se to netrefilo nikdy… Předpokládám, že modifikace toho setu probíhala z jiných vláken, než která používala ten iterátor, takže prostě každé vlákno mělo svou lokální hodnotu počtu modifikací a nikdy se to nepotkalo.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #48 kdy: 13. 04. 2018, 16:59:45 »
Mně teda ale hlavně překvapuje, pokud tam Ondřej ty iterátory normálně používá, že to nikdy nevypadlo na ConcurrentModificationException. Že je to jenom taková nouzová pojistka, která ve vícevláknovém prostředí nemusí fungovat správně, je jasné, ale že se to netrefilo nikdy… Předpokládám, že modifikace toho setu probíhala z jiných vláken, než která používala ten iterátor, takže prostě každé vlákno mělo svou lokální hodnotu počtu modifikací a nikdy se to nepotkalo.

Je otázka, jak často iterator běhal ve srovnání s modifikacemi. Nicméně obecně, ConcurrentModificationException se detekuje podle počítadla modifikací a ten, než se zpropaguje z jednoho CPU cache do druhého, tak už je typicky dávno po iteraci...

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #49 kdy: 13. 04. 2018, 19:01:25 »
Nejprve si musíte určit, co vlastně znamená, že se jedná o ten samý objekt. Ale pokud vám z toho vyjde, že jeden objekt (konkrétní místo v paměti) může v průběhu své životnosti představovat několik různých objektů (hashCode a equals zahrnují položky, které se mohou změnit), nebude vám to s většinou kolekcí fungovat.

Tohle ale s vícevláknovým přístupem nesouvisí. Vám ten vícevláknový přístup nejspíš rozbil vnitřní implementaci HashMapy použitou v tom LinkedHashSetu. To by modifikovatelné prvky kolekce nezpůsobily, ty by mohly způsobit nanejvýš to, že byste do kolekce nějaký prvek vložil, ale on by tam později nebyl, nebo byste do kolekce vložil prvek, ale později byste tam našel jiný.

Ano, akorát s tím vícevláknovým přístupem to přece jen asi trochu souvisí, i když volně - a to tak, že pokud sdílím takhle napříč aplikací objekty, je větší šance, že budou někde modifikovány (kvantitativní, nikoli kvalitativní souvislost).

Modifikace se položek se nicméně děje zcela vyjimečně a věřím, že framework implementuje equals, hashCode a compareTo rozumně (konkrétně identitu dělá podle UUID objektu a compare podle řadící anotace, hashCode teď nevím). Kolekce fungují a nanejvýš můžu mít v setu chvíli neaktualizovanou položku, což mi nevadí.

Mně teda ale hlavně překvapuje, pokud tam Ondřej ty iterátory normálně používá, že to nikdy nevypadlo na ConcurrentModificationException. Že je to jenom taková nouzová pojistka, která ve vícevláknovém prostředí nemusí fungovat správně, je jasné, ale že se to netrefilo nikdy… Předpokládám, že modifikace toho setu probíhala z jiných vláken, než která používala ten iterátor, takže prostě každé vlákno mělo svou lokální hodnotu počtu modifikací a nikdy se to nepotkalo.

Já sám iterátory v kódu přímo nepoužívám (mimo toho testovacího, který jsem zkopíroval z interaktivní vývojové konzole). Ten set je bokem mimo framework jen proto, abych z jiné větší kolekce, kterou framework již normálně managuje, vybral část a uložil do toho setu bokem. Z něho pak po jedné odebírám položky a když položky dojdou, tak to zopakuju a zase jej naplním. Pokud se zdrojová kolekce změní, provede se merge položek - položky odstraněné ze zdrojové kolekce zmizí i ze setu. LinkedHashSet je tam proto, aby tam byla každá položka jen jednou a pamatovalo se pořadí vložení. Operace nad setem jsou tedy remove(), clear() a add() bez použití iterátoru.

tady ale nebyl problém s prvky té kolekce, ale s tou kolekcí jako takovou.

Je to tak, skutečně se ten set dostal do nekonzistentního stavu - protože není thread safe a nezajistil jsem správnou synchronizaci. Původně jsem spíš myslel že bude problém v identitě prvků setu ale nakonec jsem s jistotou ověřil, že to je v té synchronizaci.


Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #50 kdy: 14. 04. 2018, 03:23:09 »
Bavit se o tom, ze kdyz to pouzivas mezi nekolika thready a hrat si na to, ze rozumis JMM a visibility vic nez Doug Lea je zcestny. Nejdriv to udelej safe - reading i writing. Jestli se modifikujou elementy kolekce, tak musi byt samozrejme synchronizovany pres stejny monitor.

Necetl jsem vsechny odpovedi

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #51 kdy: 14. 04. 2018, 09:01:17 »
Jestli se modifikujou elementy kolekce, tak musi byt samozrejme synchronizovany pres stejny monitor.
Jestli tím myslíte změny vnitřního stavu prvků kolekce, pak to není pravda – právě naopak, takové změny nesmí být synchronizovány přes stejný monitor, mohlo by dojít k uváznutí. Takové změny musejí být synchronizovány s ohledem na to, jak se s těmi objekty pracuje – většinou asi každý prvek bude mít svůj vlastní monitor. Je dokonce možné, že synchronizované vůbec být nemusí – pokud se s každým prvkem té kolekce pracuje jen v jednom vláknu.

Uvědomte si, že do kolekcí se dávají prvky typu Object, je tak nastavený i kontrakt kolekcí. Takže kolekci změny čehokoli, co je mimo Object, nezajímají. V Object je hashCode() a equals(), které pro změnu musí zůstat neměnné po celou dobu existence objektu v kolekci, bez toho by většina kolekcí fungovala špatně – i v jednovláknovém kódu. Nebo-li změny stavu objektu, které by vyvolaly změnu hashCode() nebo equals() by byly špatně i v jednovláknovém kódu a žádná synchronizace tomu nepomůže, a jakékoli jiné změny kolekci nezajímají, nedozví se o nich a tudíž nemusí být kvůli kolekci synchronizovány.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #52 kdy: 14. 04. 2018, 15:35:41 »
Bavit se o tom, ze kdyz to pouzivas mezi nekolika thready a hrat si na to, ze rozumis JMM a visibility vic nez Doug Lea je zcestny. Nejdriv to udelej safe - reading i writing. Jestli se modifikujou elementy kolekce, tak musi byt samozrejme synchronizovany pres stejny monitor.

Necetl jsem vsechny odpovedi

Nerozumím, na koho to je reakce? Jinak myslím, že problém je vyřešen a téma vyčerpáno.

Jestli se modifikujou elementy kolekce, tak musi byt samozrejme synchronizovany pres stejny monitor.
Jestli tím myslíte změny vnitřního stavu prvků kolekce, pak to není pravda – právě naopak, takové změny nesmí být synchronizovány přes stejný monitor, mohlo by dojít k uváznutí. Takové změny musejí být synchronizovány s ohledem na to, jak se s těmi objekty pracuje – většinou asi každý prvek bude mít svůj vlastní monitor. Je dokonce možné, že synchronizované vůbec být nemusí – pokud se s každým prvkem té kolekce pracuje jen v jednom vláknu.

Uvědomte si, že do kolekcí se dávají prvky typu Object, je tak nastavený i kontrakt kolekcí. Takže kolekci změny čehokoli, co je mimo Object, nezajímají. V Object je hashCode() a equals(), které pro změnu musí zůstat neměnné po celou dobu existence objektu v kolekci, bez toho by většina kolekcí fungovala špatně – i v jednovláknovém kódu. Nebo-li změny stavu objektu, které by vyvolaly změnu hashCode() nebo equals() by byly špatně i v jednovláknovém kódu a žádná synchronizace tomu nepomůže, a jakékoli jiné změny kolekci nezajímají, nedozví se o nich a tudíž nemusí být kvůli kolekci synchronizovány.

Pan Jirsák má pravdu, to lze snad těžko rozporovat.

Jenom doplnění - k modifikacím Image nedochází přímo u prvků té kolekce, může k nim dojít jen v jiných instancích stejné entity jinde v kódu (tj. jiná proměnná, jiné místo v paměti, stejné UUID entity a stejný záznam v databázi). V kolekci pak zůstane neaktuální verze entity (u entit se používá eager loading). Neaktuální verze v kolekci ale ničemu nevadí, protože za chvíli bude aplikací použita a z kolekce odstraněna (tak funguje daná aplikační logika). Později se již do kolekce načte čerstvá verze. V tomto schématu by tedy podle mě mohlo dojít v nejhorším k tomu, že by v kolekci byla stejná entita 2x (pokud by se modifikací změnila identita, k čemuž ale v použitím frameworku nedojde). Takže synchronizovat tento druh modifikace není potřeba.

Kit

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #53 kdy: 14. 04. 2018, 16:14:15 »
Jenom doplnění - k modifikacím Image nedochází přímo u prvků té kolekce, může k nim dojít jen v jiných instancích stejné entity jinde v kódu (tj. jiná proměnná, jiné místo v paměti, stejné UUID entity a stejný záznam v databázi). V kolekci pak zůstane neaktuální verze entity (u entit se používá eager loading). Neaktuální verze v kolekci ale ničemu nevadí, protože za chvíli bude aplikací použita a z kolekce odstraněna (tak funguje daná aplikační logika). Později se již do kolekce načte čerstvá verze. V tomto schématu by tedy podle mě mohlo dojít v nejhorším k tomu, že by v kolekci byla stejná entita 2x (pokud by se modifikací změnila identita, k čemuž ale v použitím frameworku nedojde). Takže synchronizovat tento druh modifikace není potřeba.

To je fakt tak těžké si obalit kolekci proxy vrstvou, která synchronizovaně zajistí všechny potřebné služby Image a nepřipustí přímou manipulaci s kolekcí?

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #54 kdy: 14. 04. 2018, 18:22:53 »
Jestli tím myslíte změny vnitřního stavu prvků kolekce, pak to není pravda – právě naopak, takové změny nesmí být synchronizovány přes stejný monitor, mohlo by dojít k uváznutí. Takové změny musejí být synchronizovány s ohledem na to, jak se s těmi objekty pracuje – většinou asi každý prvek bude mít svůj vlastní monitor. Je dokonce možné, že synchronizované vůbec být nemusí – pokud se s každým prvkem té kolekce pracuje jen v jednom vláknu.

Proc se bavime o single thread? Proc by melo dojit k uvaznuti (asi deadlock?)? Jak todle resi Collections.synchornized*? Jak happened-before je reseno v SynchronousQueue a Executors Framework?

Uvědomte si, že do kolekcí se dávají prvky typu Object, je tak nastavený i kontrakt kolekcí. Takže kolekci změny čehokoli, co je mimo Object, nezajímají. V Object je hashCode() a equals(), které pro změnu musí zůstat neměnné po celou dobu existence objektu v kolekci, bez toho by většina kolekcí fungovala špatně – i v jednovláknovém kódu. Nebo-li změny stavu objektu, které by vyvolaly změnu hashCode() nebo equals() by byly špatně i v jednovláknovém kódu a žádná synchronizace tomu nepomůže, a jakékoli jiné změny kolekci nezajímají, nedozví se o nich a tudíž nemusí být kvůli kolekci synchronizovány.

Ne, do kolekce se nedavaji Object. Davaji se tam jakykoliv tridy. U jakych kolekci je takhle nastavenej konktrakt (ja to vim)? Takze podle tvoji logiky nesmim menit zadny objekt, ktery je v kolekci? To by bylo asi hodne omezeny pouziti :)

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #55 kdy: 14. 04. 2018, 18:48:25 »
Proc se bavime o single thread?
V té části, kterou jste citoval, se bavíme o multihtread.

Proc by melo dojit k uvaznuti (asi deadlock?)?
Protože se jedním zámkem pokrývají dvě nesouvisející operace. Ale je pravda, že to samo k uváznutí nepovede, k tomu jsou potřeba dva zámky.

Jak todle resi Collections.synchornized*?
Řeší to správně, synchronizují jen kolekce a o jednotlivé prvky kolekcí se nestarají.

Jak happened-before je reseno v SynchronousQueue a Executors Framework?
Pomocí běžných synchronizačních primitiv, které jsou v Javě dostupné.

Ne, do kolekce se nedavaji Object. Davaji se tam jakykoliv tridy.
Ne, do kolekcí se nedávají třídy, dávají se tam instance. Přičemž rozhraní kolekcí vyžaduje, aby to byly instance třídy Object, tedy od těch instancí nemohou vyžadovat žádné zvláštní chování nad rámec třídy Object.

U jakych kolekci je takhle nastavenej konktrakt (ja to vim)?
hashCode() překvapivě u kolekcí, které používají hash objektu, tedy všechny kolekce, které mají v názvu „Hash“, s výjimkou identityHashMap. equals() u všech kolekcí, pokud není řečeno jinak – přičemž pokud vím, jinak je to u kolekcí v JDK pouze (opět) u IdentityHashMap, která používá identitu objektů, nikoli equals().

Takze podle tvoji logiky nesmim menit zadny objekt, ktery je v kolekci? To by bylo asi hodne omezeny pouziti :)
Ne, psal jsem pravý opak, že prvky v kolekci se mohou modifikovat libovolně, pokud to neovlivní hashCode() nebo equals().

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #56 kdy: 14. 04. 2018, 19:05:51 »
Proc se bavime o single thread?
V té části, kterou jste citoval, se bavíme o multihtread.
Single thread si do toho zamotal ty.

Proc by melo dojit k uvaznuti (asi deadlock?)?
Protože se jedním zámkem pokrývají dvě nesouvisející operace. Ale je pravda, že to samo k uváznutí nepovede, k tomu jsou potřeba dva zámky.
Takze, kdyz nactu z ConcurrentHashMap prvek, zmenim ho a dam ho zpatky (se stejnym klicem), zajistil jsem viditelnost zmen v jinych trehaded nebo ne? Muzu zmenit ten prvek (ne klic)?

Jak todle resi Collections.synchornized*?
Řeší to správně, synchronizují jen kolekce a o jednotlivé prvky kolekcí se nestarají.
Staraji. Kazdy volani zajistuje uplatneni happened-before.

Jak happened-before je reseno v SynchronousQueue a Executors Framework?
Pomocí běžných synchronizačních primitiv, které jsou v Javě dostupné.
Prave. Kdyz neco pridam do fronty, tak vsechny zmeny udelany predtim (i tech prvku), tak se uplatni.

Ne, do kolekce se nedavaji Object. Davaji se tam jakykoliv tridy.
Ne, do kolekcí se nedávají třídy, dávají se tam instance. Přičemž rozhraní kolekcí vyžaduje, aby to byly instance třídy Object, tedy od těch instancí nemohou vyžadovat žádné zvláštní chování nad rámec třídy Object.
:)
U jakych kolekci je takhle nastavenej konktrakt (ja to vim)?
hashCode() překvapivě u kolekcí, které používají hash objektu, tedy všechny kolekce, které mají v názvu „Hash“, s výjimkou identityHashMap. equals() u všech kolekcí, pokud není řečeno jinak – přičemž pokud vím, jinak je to u kolekcí v JDK pouze (opět) u IdentityHashMap, která používá identitu objektů, nikoli equals().

Takze podle tvoji logiky nesmim menit zadny objekt, ktery je v kolekci? To by bylo asi hodne omezeny pouziti :)
Ne, psal jsem pravý opak, že prvky v kolekci se mohou modifikovat libovolně, pokud to neovlivní hashCode() nebo equals().
Takhle to nevyznelo. Ale beru opravu.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #57 kdy: 14. 04. 2018, 19:08:46 »
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).

Tomu se rika race condition. Brian Goetz napsal skvelou knizku https://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601. Je ted ve sleve za $15. Stalo by zato si ji precist nez delat zavery, ze kdyz neco modifikuju pres nekolik threadu bez synchronizace, tak to nemuzu rozbit. Proto jsem psal, ze si hrajes na Doug Lea

Kit

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #58 kdy: 14. 04. 2018, 19:27:05 »
Ne, do kolekce se nedavaji Object. Davaji se tam jakykoliv tridy. U jakych kolekci je takhle nastavenej konktrakt (ja to vim)? Takze podle tvoji logiky nesmim menit zadny objekt, ktery je v kolekci? To by bylo asi hodne omezeny pouziti :)

Do kolekce se nedávají třídy, ale objekty splnující deklarované rozhraní.

Re:Java: Mám neprázdný Set, ale nemůžu z něj dostat položky
« Odpověď #59 kdy: 14. 04. 2018, 19:32:31 »
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).

Tomu se rika race condition. Brian Goetz napsal skvelou knizku https://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601. Je ted ve sleve za $15. Stalo by zato si ji precist nez delat zavery, ze kdyz neco modifikuju pres nekolik threadu bez synchronizace, tak to nemuzu rozbit. Proto jsem psal, ze si hrajes na Doug Lea

Aha, dobře, díky za vysvětlení příspěvku. Problém nebyla absence synchronizace, ale špatná synchronizace (přes špatný objekt). Závěry jsem nedělal, ptal jsem se a odpověď jsem dostal již na cca druhé stránce této diskuze, totiž že se rozjede počítadlo se skutečným počtem prvků. Tímto je téma de fakto vyčerpáno, z literatury jsem si stáhnul Java Concurrency in Practice. Za mě už v debatě končím, klidně tu ale pokračujte  :)