Kirish
Biz allaqachon birinchi qismda iplar qanday yaratilganligini ko'rib chiqdik . Yana eslaylik.
Tarmoq - bu
Thread
unda ishlaydigan narsa , shuning uchun keling
, tutorialspoint java onlayn kompilyatoridanrun
foydalanamiz va quyidagi kodni bajaramiz:
public class HelloWorld {
public static void main(String []args){
Runnable task = () -> {
System.out.println("Hello World");
};
new Thread(task).start();
}
}
Bu mavzudagi vazifani bajarish uchun yagona variantmi?
java.util.concurrent.Callable
Ma'lum bo'lishicha,
java.lang.Runnable ning akasi bor va uning ismi
java.util.concurrent.Callable va u Java 1.5 da tug'ilgan. Qanday farqlar bor?
Runnable
Agar biz ushbu interfeysning JavaDoc-ni diqqat bilan ko'rib chiqsak, dan farqli o'laroq , yangi interfeys
call
natijani qaytaradigan usulni e'lon qilishini ko'ramiz . Bundan tashqari, sukut bo'yicha u Istisnoni chiqaradi.
try-catch
Ya'ni, bu bizni tekshirilgan istisnolar uchun bloklarni yozish zaruratidan qutqaradi . Allaqachon yomon emas, to'g'rimi? Endi bizda
Runnable
yangi vazifa bor:
Callable task = () -> {
return "Hello, World!";
};
Lekin u bilan nima qilish kerak? Nega bizga natijani qaytaradigan ish zarrachasida ishlaydigan vazifa kerak? Shubhasiz, kelajakda biz kelajakda amalga oshiriladigan harakatlarning natijasini olishni kutmoqdamiz. Ingliz tilida kelajak - kelajak. Va aynan bir xil nomga ega interfeys mavjud:
java.util.concurrent.Future
java.util.concurrent.Future
Java.util.concurrent.Future interfeysi biz natijalarini kelajakda olishni rejalashtirgan vazifalar bilan ishlash uchun APIni tavsiflaydi: natijalarni olish usullari, holatni tekshirish usullari. Biz
Future
uni amalga oshirishga qiziqamiz
java.util.concurrent.FutureTask . Ya'ni
Task
, bu ichida ijro etiladigan narsa
Future
. Ushbu amalga oshirishning qiziq tomoni shundaki, u amalga oshiradi va
Runnable
. Buni iplardagi vazifalar bilan ishlashning eski modeli va yangi modelning o'ziga xos adapteri deb hisoblashingiz mumkin (java 1.5 da paydo bo'lgan ma'noda yangi). Mana bir misol:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class HelloWorld {
public static void main(String []args) throws Exception {
Callable task = () -> {
return "Hello, World!";
};
FutureTask<String> future = new FutureTask<>(task);
new Thread(future).start();
System.out.println(future.get());
}
}
Misoldan ko'rinib turibdiki, usuldan foydalanib,
get
muammoning natijasini olamiz
task
.
(!) Muhim, usul yordamida natija olingan paytda,
get
bajarilish sinxron bo'ladi. Sizningcha, bu erda qanday mexanizm qo'llaniladi? To'g'ri, sinxronizatsiya bloki yo'q - shuning uchun biz JVisualVM-da
WAITING-monitor
ni yoki sifatida emas
wait
, balki xuddi shu tarzda ko'ramiz
park
(chunki mexanizm ishlatiladi
LockSupport
).
Funktsional interfeyslar
Keyin biz Java 1.8 dan darslar haqida gaplashamiz, shuning uchun qisqacha kirish qilish foydali bo'ladi. Keling, quyidagi kodni ko'rib chiqaylik:
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "String";
}
};
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
Function<String, Integer> converter = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
};
Ko'p keraksiz kod bor, shunday emasmi? E'lon qilingan sinflarning har biri bitta funktsiyani bajaradi, ammo uni tavsiflash uchun biz keraksiz yordamchi kodlardan foydalanamiz. Java ishlab chiquvchilari ham shunday deb o'ylashgan. Shuning uchun ular "funktsional interfeyslar" to'plamini (
@FunctionalInterface
) taqdim etdilar va endi Java-ning o'zi biz uchun hamma narsani "o'ylab ko'radi" degan qarorga keldi, muhimlaridan tashqari:
Supplier<String> supplier = () -> "String";
Consumer<String> consumer = s -> System.out.println(s);
Function<String, Integer> converter = s -> Integer.valueOf(s);
Supplier
- provayder. Uning parametrlari yo'q, lekin u nimanidir qaytaradi, ya'ni uni etkazib beradi.
Consumer
- iste'molchi. U kirish (parametr s) sifatida biror narsani oladi va u bilan biror narsa qiladi, ya'ni biror narsani iste'mol qiladi. Yana bir funktsiya mavjud. U kirish (parametr) sifatida biror narsani oladi
s
, biror narsa qiladi va biror narsani qaytaradi. Ko'rib turganimizdek, generiklar faol qo'llaniladi. Agar ishonchingiz komil bo'lmasa, ularni eslab, "
Java-da generiklar nazariyasi yoki amalda qavslarni qanday qo'yish kerak " ni o'qishingiz mumkin.
CompletableFuture
Vaqt o'tishi bilan Java 1.8 yangi sinfni taqdim etdi
CompletableFuture
. U interfeysni amalga oshiradi
Future
, ya'ni bizniki
task
kelajakda bajariladi va biz bajarishimiz
get
va natijaga erishishimiz mumkin. Lekin u ham ba'zilarini amalga oshiradi
CompletionStage
. Tarjimadan uning maqsadi allaqachon aniq: bu qandaydir hisob-kitobning ma'lum bir bosqichidir. Mavzuga qisqacha kirishni "
ComletionStage va CompletableFuture to'g'risida " umumiy ko'rinishda topish mumkin. Keling, to'g'ridan-to'g'ri mavzuga o'taylik. Ishni boshlashimizga yordam beradigan mavjud statik usullar ro'yxatini ko'rib chiqaylik:
Quyida ulardan foydalanish variantlari keltirilgan:
import java.util.concurrent.CompletableFuture;
public class App {
public static void main(String []args) throws Exception {
CompletableFuture<String> completed;
completed = CompletableFuture.completedFuture("Просто meaning");
CompletableFuture<Void> voidCompletableFuture;
voidCompletableFuture = CompletableFuture.runAsync(() -> {
System.out.println("run " + Thread.currentThread().getName());
});
CompletableFuture<String> supplier;
supplier = CompletableFuture.supplyAsync(() -> {
System.out.println("supply " + Thread.currentThread().getName());
return "Значение";
});
}
}
CompletableFuture
Agar biz ushbu kodni ishlatsak, yaratilish butun zanjirni boshlashni o'z ichiga olishini ko'ramiz . Shuning uchun, Java8 dan SteamAPI bilan o'xshashlik mavjud bo'lsa-da, bu yondashuvlar orasidagi farq. Masalan:
List<String> array = Arrays.asList("one", "two");
Stream<String> stringStream = array.stream().map(value -> {
System.out.println("Executed");
return value.toUpperCase();
});
Bu Java 8 Stream Api-ning namunasidir (bu haqida ko'proq ma'lumotni bu erda o'qishingiz mumkin "
Java 8 Stream API qo'llanmasi rasmlar va misollar "). Agar siz ushbu kodni ishga tushirsangiz, u
Executed
ko'rsatilmaydi. Ya'ni, Java-da oqim yaratishda oqim darhol boshlanmaydi, lekin undan qiymat kerak bo'lguncha kutadi. Lekin
CompletableFuture
u hisoblangan qiymat so'ralishini kutmasdan, darhol bajarish uchun zanjirni boshlaydi. Menimcha, buni tushunish muhim. Shunday qilib, bizda CompletableFuture bor. Qanday qilib biz zanjir yaratishimiz mumkin va bizda qanday vositalar bor? Keling, avvalroq yozgan funktsional interfeyslarni eslaylik.
- Bizda ( ) funksiyasi bor
Function
, u A ni oladi va B ni qaytaradi. Uning bitta usuli bor - apply
(qo'llash).
- Bizda iste'molchi (
Consumer
) mavjud bo'lib, u A ni qabul qiladi va hech narsa qaytarmaydi ( Void ). Uning faqat bitta usuli bor - accept
(qabul qiling).
Runnable
Bizda qabul qilinmaydigan yoki qaytarilmaydigan mavzuda ishlaydigan kod mavjud . Uning bitta usuli bor - run
(yugurish).
Esda tutish kerak bo'lgan ikkinchi narsa shundaki,
CompletalbeFuture
u o'z ishida
Runnable
iste'molchilar va funktsiyalardan foydalanadi. Buni hisobga olgan holda, buni har doim qilishingiz mumkinligini eslab qolishingiz mumkin
CompletableFuture
:
public static void main(String []args) throws Exception {
AtomicLong longValue = new AtomicLong(0);
Runnable task = () -> longValue.set(new Date().getTime());
Function<Long, Date> dateConverter = (longvalue) -> new Date(longvalue);
Consumer<Date> printer = date -> {
System.out.println(date);
System.out.flush();
};
CompletableFuture.runAsync(task)
.thenApply((v) -> longValue.get())
.thenApply(dateConverter)
.thenAccept(printer);
}
Usullarning versiyalari
thenRun
bor . _ Bu shuni anglatadiki, bu bosqichlar yangi ipda bajariladi. U maxsus hovuzdan olinadi, shuning uchun yangi yoki eski qanday oqim bo'lishi oldindan ma'lum emas. Hammasi vazifalar qanchalik qiyinligiga bog'liq. Ushbu usullardan tashqari yana uchta qiziqarli imkoniyat mavjud. Aniqlik uchun, bizda biron bir joydan xabar oladigan ma'lum bir xizmat borligini tasavvur qilaylik va bu vaqt talab etadi:
thenApply
thenAccept
Async
public static class NewsService {
public static String getMessage() {
try {
Thread.currentThread().sleep(3000);
return "Message";
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
Endi boshqa xususiyatlarni ko'rib chiqaylik
CompletableFuture
. Natijani
CompletableFuture
boshqa natija bilan birlashtira olamiz
CompletableFuture
:
Supplier newsSupplier = () -> NewsService.getMessage();
CompletableFuture<String> reader = CompletableFuture.supplyAsync(newsSupplier);
CompletableFuture.completedFuture("!!")
.thenCombine(reader, (a, b) -> b + a)
.thenAccept(result -> System.out.println(result))
.get();
Shuni ta'kidlash kerakki, sukut bo'yicha iplar daemon iplari bo'ladi, shuning uchun aniqlik uchun
get
biz natijani kutish uchun foydalanamiz. Va biz nafaqat birlashtira olamiz (birlashtiramiz), balki qaytarishimiz mumkin
CompletableFuture
:
CompletableFuture.completedFuture(2L)
.thenCompose((val) -> CompletableFuture.completedFuture(val + 2))
.thenAccept(result -> System.out.println(result));
Bu erda shuni ta'kidlashni istardimki, qisqachalik uchun usul ishlatilgan
CompletableFuture.completedFuture
. Bu usul yangi ipni yaratmaydi, shuning uchun zanjirning qolgan qismi u chaqirilgan ipda bajariladi
completedFuture
. Usul ham bor
thenAcceptBoth
. U ga juda o'xshaydi
accept
, lekin
thenAccept
qabul qilsa
consumer
,
thenAcceptBoth
u boshqa
CompletableStage
+ kirish sifatida qabul qiladi
BiConsumer
, ya'ni
consumer
kirish sifatida bitta emas, 2 ta manbani qabul qiladi. So'z bilan yana bir qiziqarli imkoniyat mavjud
Either
:
Bu usullar muqobilni qabul qiladi va birinchi bo'lib bajarilgan
CompletableStage
usulda amalga oshiriladi .
CompletableStage
Va men ushbu sharhni yana bir qiziqarli xususiyat bilan yakunlamoqchiman
CompletableFuture
- xatolarni qayta ishlash.
CompletableFuture.completedFuture(2L)
.thenApply((a) -> {
throw new IllegalStateException("error");
}).thenApply((a) -> 3L)
.thenAccept(val -> System.out.println(val));
Bu kod hech narsa qilmaydi, chunki ... istisno tashlanadi va hech narsa bo'lmaydi. Ammo agar biz izohni bekor qilsak
exceptionally
, unda biz xatti-harakatni aniqlaymiz. Shuningdek, ushbu mavzu bo'yicha
CompletableFuture
quyidagi videoni tomosha qilishni tavsiya etaman :
Mening kamtarona fikrimcha, bu videolar Internetdagi eng ko'p ingl. Ulardan hammasi qanday ishlashi, bizda qanday arsenal borligi va nima uchun kerakligi aniq bo'lishi kerak.
Xulosa
Umid qilamanki, hisob-kitoblar hisoblangandan keyin qanday qilib iplardan foydalanish mumkinligi aniq bo'ldi. Qo'shimcha material:
#Viacheslav
GO TO FULL VERSION