The Codest
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Odvětví
    • Fintech a bankovnictví
    • E-commerce
    • Adtech
    • Healthtech
    • Výroba
    • Logistika
    • Automobilový průmysl
    • IOT
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
Šipka zpět ZPĚT
2022-06-15
Vývoj softwaru

Souběžnost v jazyce Java - část 1 - úvod

The Codest

Rafal Sawicki

Vývojář v jazyce Java

Přečtěte si první část našeho seriálu blogů věnovaného souběhu v jazyce Java. V následujícím článku se blíže podíváme na rozdíly mezi vlákny a procesy, pooly vláken, exekutory a mnoho dalšího!

Obecně platí, že konvenční programování je sekvenční. Vše v programu se děje postupně.
Ve skutečnosti však paralelně běží celý svět - je to schopnost vykonávat více úloh současně.

Vlákno vs. proces

Diskutovat o pokročilých tématech, jako jsou souběžnost v Java nebo multithreadingu, musíme se dohodnout na některých společných definicích, abychom si byli jisti, že jsme na stejné vlně.

Začněme od základů. V nesekvenčním světě máme dva druhy reprezentantů souběhu: procesy a souběhy.
závity. Proces je instance běžícího programu. Obvykle je izolován od ostatních procesů.
Za přidělování prostředků jednotlivým procesům je zodpovědný operační systém. Kromě toho funguje jako vodič, který
plánuje a kontroluje.

Nit je druh procesu, ale na nižší úrovni, proto je také známá jako lehká nit. V jednom vlákně může běžet více vláken
proces. Program zde funguje jako plánovač a řadič vláken. Tímto způsobem se jednotlivé programy jeví jako
více úkolů najednou.

Základním rozdílem mezi vlákny a procesy je úroveň izolace. Proces má svou vlastní sadu
zdrojů, zatímco vlákno sdílí data s ostatními vlákny. Může se zdát, že jde o přístup náchylný k chybám, a skutečně tomu tak je. Pro
nyní se na to nezaměřujme, protože to přesahuje rámec tohoto článku.

Procesy, vlákna - dobře... Ale co přesně je souběžnost? Souběžnost znamená, že můžete provádět více úloh najednou.
čas. Neznamená to, že tyto úlohy musí běžet současně - k tomu slouží paralelismus. Concurrenc v Javay také ne
vyžadovat více procesorů nebo dokonce více jader. Toho lze dosáhnout v prostředí s jedním jádrem s využitím
přepínání kontextu.

Termínem souvisejícím se souběžností je multithreading. Jedná se o vlastnost programů, která jim umožňuje vykonávat několik úloh najednou. Ne každý program tento přístup používá, ale ty, které jej používají, lze označit za vícevláknové.

Jsme téměř připraveni, už jen jedna definice. Asynchronnost znamená, že program provádí neblokující operace.
Iniciuje úkol a pak se věnuje dalším činnostem, zatímco čeká na odpověď. Když odpověď dostane, může reagovat k němu.

Všechen ten jazz

Ve výchozím nastavení je každý Aplikace Java běží v jednom procesu. V tomto procesu je jedno vlákno související s procesem main() metoda
aplikace. Jak však bylo uvedeno, je možné využít mechanismy více vláken v rámci jednoho vlákna.
program.

Spustitelné

Vlákno je Java třída, ve které se odehrává kouzlo. Jedná se o objektovou reprezentaci výše zmíněného vlákna. Na adresu
vytvořit vlastní vlákno, můžete rozšířit Vlákno třída. Není to však doporučený přístup. Vlákna by měl být použit jako mechanismus pro spuštění úlohy. Úlohy jsou části kód které chceme spustit v souběžném režimu. Můžeme je definovat pomocí příkazu Spustitelné rozhraní.

Ale dost teorie, pojďme se podívat na náš kód.

Problém

Předpokládejme, že máme několik polí čísel. Pro každé pole chceme znát součet čísel v poli. Nechť
předstírat, že takových polí je mnoho a každé z nich je poměrně velké. Za takových podmínek chceme využít souběhu a každé pole sečíst jako samostatnou úlohu.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Runnable task1 = () -> {
    int sum = Arrays.stream(a1).sum();
    System.out.println("1. Součet je: " + sum);
};

Runnable task2 = () -> {
    int sum = Arrays.stream(a2).sum();
    System.out.println("2. Součet je: " + sum);
};

Runnable task3 = () -> {
    int sum = Arrays.stream(a3).sum();
    System.out.println("3. Součet je: " + sum);
};

new Thread(task1).start();
new Thread(task2).start();
new Thread(task3).start();

Jak vidíte z výše uvedeného kódu Spustitelné je funkční rozhraní. Obsahuje jedinou abstraktní metodu run()
bez argumentů. Adresa Spustitelné rozhraní by mělo být implementováno každou třídou, jejíž instance mají být
prováděné vláknem.

Po definování úlohy můžete vytvořit vlákno pro její spuštění. Toho lze dosáhnout pomocí new Thread() konstruktor, který
bere Spustitelné jako argument.

Posledním krokem je start() nově vytvořené vlákno. V rozhraní API jsou také run() metody v Spustitelné a v
Vlákno. To však není způsob, jak v Javě využít souběžnost. Přímé volání každé z těchto metod vede k tomu, že se
provádění úlohy ve stejném vlákně main() metoda se spustí.

Fondy vláken a exekutoři

Při velkém počtu úloh není dobré vytvářet pro každou z nich samostatné vlákno. Vytvoření Vlákno je
těžkou operaci a je mnohem lepší znovu použít stávající vlákna než vytvářet nová.

Pokud program vytváří mnoho krátkodobých vláken, je lepší použít fond vláken. Fond vláken obsahuje řadu
vlákna připravená ke spuštění, ale v současné době neaktivní. Poskytnutí a Spustitelné do fondu způsobí, že jedno z vláken zavolá příkaz
run() metoda daného Spustitelné. Po dokončení úlohy vlákno stále existuje a je v nečinném stavu.

Dobře, chápete to - místo ručního vytváření raději fond vláken. Ale jak můžete využít pooly vláken? Na adrese Exekutoři
třída má řadu statických továrních metod pro konstrukci poolu vláken. Například newCachedThredPool() vytváří
fond, ve kterém se podle potřeby vytvářejí nová vlákna a nečinná vlákna se uchovávají po dobu 60 sekund. Naproti tomu,
newFixedThreadPool() obsahuje pevnou množinu vláken, v níž jsou nečinná vlákna uchovávána po neomezenou dobu.

Podívejme se, jak by to mohlo fungovat v našem příkladu. Nyní již nemusíme vytvářet vlákna ručně. Místo toho musíme vytvořit
ExecutorService který poskytuje fond vláken. Pak mu můžeme přiřadit úlohy. Posledním krokem je uzavření vlákna
pool, aby nedocházelo k únikům paměti. Zbytek předchozího kódu zůstává stejný.

ExecutorService executor = Executors.newCachedThreadPool();

executor.submit(task1);
executor.submit(task2);
executor.submit(task3);

executor.shutdown();

Volatelný

Spustitelné se zdá být šikovným způsobem vytváření souběžných úloh, ale má jeden zásadní nedostatek. Nemůže vrátit žádnou
hodnotu. Navíc nemůžeme určit, zda je úkol dokončen, nebo ne. Nevíme také, zda byl dokončen.
normálně nebo výjimečně. Řešením těchto neduhů je Volatelný.

Volatelný je podobný jako Spustitelné v jistém smyslu také obaluje asynchronní úlohy. Hlavním rozdílem je, že je schopen
vrátit hodnotu. Návratová hodnota může být libovolného (neprimitivního) typu, protože typ Volatelný rozhraní je parametrizovaný typ.
Volatelný je funkční rozhraní, které má call() metoda, která může vyhodit Výjimka.

Nyní se podíváme, jak můžeme využít Volatelný v našem problému s poli.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Callable task1 = () -> Arrays.stream(a1).sum();
Callable task2 = () -> Arrays.stream(a2).sum();
Callable task3 = () -> Arrays.stream(a3).sum();

ExecutorService executor = Executors.newCachedThreadPool();
Future future1 = executor.submit(task1);
Future future2 = executor.submit(task2);
Future future3 = executor.submit(task3);

System.out.println("1. Součet je: " + future1.get());
System.out.println("2. Součet je: " + future2.get());
System.out.println("3. Součet je: " + future3.get());

executor.shutdown();

Dobře, vidíme, jak Volatelný je vytvořen a poté odeslán do ExecutorService. Ale co to sakra je Budoucnost?
Budoucnost funguje jako most mezi vlákny. Součet každého pole se vytváří v samostatném vlákně a my potřebujeme způsob, jak ho
získat tyto výsledky zpět do main().

Získání výsledku z Budoucnost musíme zavolat get() metoda. Zde se může stát jedna ze dvou věcí. Zaprvé,
výsledek výpočtu provedeného pomocí Volatelný je k dispozici. Pak ji okamžitě dostaneme. Za druhé, výsledek není
již připravena. V tom případě get() se zablokuje, dokud nebude k dispozici výsledek.

ComputableFuture

Problém s Budoucnost je, že funguje v paradigmatu "push". Při použití Budoucnost musíte být jako šéf, který
se neustále ptá: "Je tvůj úkol splněn? Je hotový?", dokud nepřinese výsledek. Jednání pod neustálým tlakem je
drahé. Mnohem lepší by bylo objednat si Budoucnost co má udělat, až bude připraven ke svému úkolu. Bohužel,
Budoucnost to nemůže udělat, ale ComputableFuture může.

ComputableFuture funguje v "pull paradigmatu". Můžeme mu říci, co má udělat s výsledkem, když splní své úkoly. To
je příkladem asynchronního přístupu.

ComputableFuture funguje perfektně s Spustitelné ale ne s Volatelný. Místo toho je možné zadat úlohu do
ComputableFuture ve formě Dodavatel.

Podívejme se, jak výše uvedené souvisí s naším problémem.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

CompletableFuture.supplyAsync(() -> Arrays.stream(a1).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a2).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a3).sum())
                .thenAccept(System.out::println);

První, co vás zarazí, je, o kolik je toto řešení kratší. Kromě toho také vypadá elegantně a úhledně.

Úkol pro DokončitelnéBudoucnost může poskytnout supplyAsync() metoda, která přebírá Dodavatel nebo runAsync() že
bere Spustitelné. Zpětné volání - část kódu, která má být spuštěna při dokončení úlohy - je definováno příkazem thenAccept()
metoda.

Závěry

Java nabízí mnoho různých přístupů k souběhu. V tomto článku jsme se tohoto tématu sotva dotkli.

Nicméně jsme se zabývali základy Vlákno, Spustitelné, Volatelnýa CallableFuture což je dobrý postřeh
pro další zkoumání tématu.

Související články

Vývoj softwaru

9 chyb, kterých se vyvarujte při programování v jazyce Java

Jakých chyb byste se měli vyvarovat při programování v jazyce Java? V následujícím díle na tuto otázku odpovíme.

The Codest
Rafal Sawicki Vývojář v jazyce Java
Podniková a škálovací řešení

Správný způsob, jak najít špičkové vývojáře Java

Najít ideálního vývojáře v jazyce Java může být náročný úkol. Vzhledem k tomu, že poptávka po těchto odbornících na trhu roste ohromujícím tempem, dostupné zdroje pro vyhledávání talentů se někdy mohou zdát...

The Codest
Grzegorz Rozmus Vedoucí jednotky Java
Podniková a škálovací řešení

10 dubajských společností, které se vyplatí sledovat v roce 2020

Dubaj je srdcem Spojených arabských emirátů se stále více prosperujícím trhem globálních společností a slibných startupů. Mnohé z nich se mohou pochlubit mezinárodním úspěchem a pozoruhodnými produkty.....

Tuňák Pinar
Podniková a škálovací řešení

Jak může Java podpořit vaše podnikání?

Než začneme, rád bych vám připomněl jednu důležitou věc. Java není jen programovací jazyk.

Bartlomiej Kuczynski
Podniková a škálovací řešení

Den v životě programátora ve společnosti The Codest

Možná tušíte, že pracovní rozvrhy programátorů se od sebe neliší. Ale ve skutečnosti to není pravda! Každý startup, softwarový dům, dokonce i korporace má své vlastní...

The Codest
Pawel Rybczynski Software Engineer

Přihlaste se k odběru naší znalostní databáze a získejte aktuální informace o odborných znalostech z oblasti IT.

    O nás

    The Codest - Mezinárodní společnost zabývající se vývojem softwaru s technologickými centry v Polsku.

    Spojené království - ústředí

    • Kancelář 303B, 182-184 High Street North E6 2JA
      Londýn, Anglie

    Polsko - Místní technologická centra

    • Kancelářský park Fabryczna, Aleja
      Pokoju 18, 31-564 Krakov
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšava, Polsko

      The Codest

    • Home
    • O nás
    • Služby
    • Case Studies
    • Vědět jak
    • Kariéra
    • Slovník

      Služby

    • To Advisory
    • Vývoj softwaru
    • Vývoj backendu
    • Vývoj frontendů
    • Staff Augmentation
    • Vývojáři backendu
    • Cloudoví inženýři
    • Datoví inženýři
    • Další
    • Inženýři QA

      Zdroje

    • Fakta a mýty o spolupráci s externím partnerem pro vývoj softwaru
    • Z USA do Evropy: Proč se americké startupy rozhodly přesídlit do Evropy?
    • Srovnání technických vývojových center v zahraničí: Tech Offshore Evropa (Polsko), ASEAN (Filipíny), Eurasie (Turecko)
    • Jaké jsou hlavní výzvy CTO a CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2026 by The Codest. Všechna práva vyhrazena.

    cs_CZCzech
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese es_ESSpanish nl_NLDutch etEstonian elGreek pt_PTPortuguese cs_CZCzech