JavaRush /Java blogi /Random-UZ /Siz Java-ni ip bilan buzolmaysiz: IV qism - Qo'ng'iroq qi...
Viacheslav
Daraja

Siz Java-ni ip bilan buzolmaysiz: IV qism - Qo'ng'iroq qilish mumkin, kelajak va do'stlar

Guruhda nashr etilgan

Kirish

Biz allaqachon birinchi qismda iplar qanday yaratilganligini ko'rib chiqdik . Yana eslaylik. Siz Java-ni ip bilan buzolmaysiz: IV qism - Qo'ng'iroq qilish mumkin, kelajak va do'stlar - 1Tarmoq - bu Threadunda 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? RunnableAgar biz ushbu interfeysning JavaDoc-ni diqqat bilan ko'rib chiqsak, dan farqli o'laroq , yangi interfeys callnatijani qaytaradigan usulni e'lon qilishini ko'ramiz . Bundan tashqari, sukut bo'yicha u Istisnoni chiqaradi. try-catchYa'ni, bu bizni tekshirilgan istisnolar uchun bloklarni yozish zaruratidan qutqaradi . Allaqachon yomon emas, to'g'rimi? Endi bizda Runnableyangi 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 Futureuni 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, getmuammoning natijasini olamiz task. (!) Muhim, usul yordamida natija olingan paytda, getbajarilish 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 taskkelajakda bajariladi va biz bajarishimiz getva 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: Siz Java-ni ip bilan buzolmaysiz: IV qism - Qo'ng'iroq qilish mumkin, kelajak va do'stlar - 2Quyida ulardan foydalanish variantlari keltirilgan:
import java.util.concurrent.CompletableFuture;
public class App {
    public static void main(String []args) throws Exception {
        // CompletableFuture уже содержащий результат
        CompletableFuture<String> completed;
        completed = CompletableFuture.completedFuture("Просто meaning");
        // CompletableFuture, запускающий (run) новый поток с Runnable, поэтому он Void
        CompletableFuture<Void> voidCompletableFuture;
        voidCompletableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("run " + Thread.currentThread().getName());
        });
        // CompletableFuture, запускающий новый поток, результат которого возьмём у Supplier
        CompletableFuture<String> supplier;
        supplier = CompletableFuture.supplyAsync(() -> {
            System.out.println("supply " + Thread.currentThread().getName());
            return "Значение";
        });
    }
}
CompletableFutureAgar 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 Executedko'rsatilmaydi. Ya'ni, Java-da oqim yaratishda oqim darhol boshlanmaydi, lekin undan qiymat kerak bo'lguncha kutadi. Lekin CompletableFutureu 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).
  • RunnableBizda qabul qilinmaydigan yoki qaytarilmaydigan mavzuda ishlaydigan kod mavjud . Uning bitta usuli bor - run(yugurish).
Esda tutish kerak bo'lgan ikkinchi narsa shundaki, CompletalbeFutureu o'z ishida Runnableiste'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 computation
        CompletableFuture.runAsync(task)
                         .thenApply((v) -> longValue.get())
                         .thenApply(dateConverter)
                         .thenAccept(printer);
}
Usullarning versiyalari thenRunbor . _ 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: thenApplythenAcceptAsync
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 CompletableFutureboshqa 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 getbiz 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 thenAcceptqabul qilsa consumer, thenAcceptBothu boshqa CompletableStage+ kirish sifatida qabul qiladi BiConsumer, ya'ni consumerkirish sifatida bitta emas, 2 ta manbani qabul qiladi. So'z bilan yana bir qiziqarli imkoniyat mavjud Either: Siz Java-ni ip bilan buzolmaysiz: IV qism - Qo'ng'iroq qilish mumkin, kelajak va do'stlar - 3Bu usullar muqobilni qabul qiladi va birinchi bo'lib bajarilgan CompletableStageusulda amalga oshiriladi . CompletableStageVa 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)
				 //.exceptionally(ex -> 0L)
				 .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 CompletableFuturequyidagi 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
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION