Giriş
Biz artıq birinci hissədə iplərin necə yaradıldığına baxdıq . Bir daha xatırlayaq.
Mövzu
Thread
onun içində işləyən bir şeydir , ona görə də
tutorialspoint java onlayn tərtibçisindənrun
istifadə edək və aşağıdakı kodu yerinə yetirək:
public class HelloWorld {
public static void main(String []args){
Runnable task = () -> {
System.out.println("Hello World");
};
new Thread(task).start();
}
}
Bu, mövzuda tapşırığı yerinə yetirmək üçün yeganə seçimdir?
java.util.concurrent.Callable
Məlum olub ki,
java.lang.Runnable-ın bir qardaşı var və onun adı
java.util.concurrent.Callable- dir və Java 1.5-də anadan olub. Fərqlər nələrdir? Bu interfeysin JavaDoc-a daha yaxından nəzər salsaq görərik ki, -dən fərqli olaraq
Runnable
yeni interfeys
call
nəticə qaytaran metodu elan edir. Həmçinin, standart olaraq İstisna atır.
try-catch
Yəni bizi yoxlanılan istisnalar üçün bloklar yazmaq ehtiyacından xilas edir . Onsuz da pis deyil, elə deyilmi?
Runnable
İndi əvəzinə yeni bir vəzifəmiz var :
Callable task = () -> {
return "Hello, World!";
};
Amma bununla nə etməli? Nəticəni qaytaran bir ip üzərində işləyən bir tapşırığa niyə ehtiyacımız var? Aydındır ki, biz gələcəkdə həyata keçiriləcək tədbirlərin nəticəsini gələcəkdə alacağımızı gözləyirik. İngilis dilində Future - Future. Və eyni adlı bir interfeys var:
java.util.concurrent.Future
java.util.concurrent.Future
Java.util.concurrent.Future interfeysi nəticələrini gələcəkdə əldə etməyi planlaşdırdığımız tapşırıqlarla işləmək üçün API-ni təsvir edir: nəticələrin əldə edilməsi üsulları, statusun yoxlanılması üsulları. Biz
Future
onun həyata keçirilməsində maraqlıyıq
java.util.concurrent.FutureTask . Yəni
Task
edam ediləcək şey budur
Future
. Bu həyata keçirilməsi ilə bağlı maraqlı olan da odur ki, həyata keçirir və
Runnable
. Bunu iplərdəki tapşırıqlarla işləmək üçün köhnə modelin və yeni modelin bir növ adapteri hesab edə bilərsiniz (java 1.5-də göründüyü mənada yeni). Budur bir nümunə:
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());
}
}
Nümunədən göründüyü kimi, metoddan istifadə edərək
get
problemdən nəticə alırıq
task
.
(!) Vacibdir, metoddan istifadə edərək nəticə əldə edildiyi anda
get
icra sinxronlaşır. Sizcə, burada hansı mexanizmdən istifadə olunacaq? Düzdür, sinxronizasiya bloku yoxdur - buna görə də biz JVisualVM-də
GÖZLƏMƏnimonitor
və ya kimi deyil
wait
, eynisi kimi görəcəyik
park
(mexanizm istifadə edildiyi üçün
LockSupport
).
Funksional interfeyslər
Sonra Java 1.8-dən olan dərslər haqqında danışacağıq, ona görə də qısa bir giriş etmək faydalı olardı. Aşağıdakı koda baxaq:
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);
}
};
Çoxlu lazımsız kodlar var, elə deyilmi? Elan edilmiş siniflərin hər biri bir funksiyanı yerinə yetirir, lakin onu təsvir etmək üçün bir dəstə lazımsız köməkçi koddan istifadə edirik. Java tərtibatçıları da belə düşünürdülər. Buna görə də, onlar bir sıra "funksional interfeyslər" (
@FunctionalInterface
) təqdim etdilər və qərara gəldilər ki, indi Java özü bizim üçün vacib olanlardan başqa hər şeyi "düşünəcək":
Supplier<String> supplier = () -> "String";
Consumer<String> consumer = s -> System.out.println(s);
Function<String, Integer> converter = s -> Integer.valueOf(s);
Supplier
- provayder. Parametrləri yoxdur, amma nəyisə qaytarır, yəni onu təmin edir.
Consumer
- istehlakçı. Giriş kimi nəyisə götürür (parametr s) və onunla nəsə edir, yəni nəyisə istehlak edir. Başqa bir funksiya var. O, bir şeyi giriş (parametr
s
) kimi qəbul edir, bir şey edir və nəyisə qaytarır. Gördüyümüz kimi, generiklər fəal şəkildə istifadə olunur. Əgər əmin deyilsinizsə, onları xatırlayıb “
Java-da generiklər nəzəriyyəsi və ya praktikada mötərizələrin necə qoyulması ” bölməsini oxuya bilərsiniz.
Tamamlana bilən Gələcək
Zaman keçdikcə Java 1.8 adlı yeni bir sinif təqdim etdi
CompletableFuture
. O, interfeysi həyata keçirir
Future
, yəni bizimkilər
task
gələcəkdə icra olunacaq və biz icra edib
get
nəticə əldə edə bilərik. Amma bəzilərini də həyata keçirir
CompletionStage
. Tərcümədən onun məqsədi artıq aydındır: bu, bir növ hesablamanın müəyyən bir mərhələsidir.
Mövzuya qısa girişi " ComletionStage və CompletableFuture-a Giriş " icmalında tapa bilərsiniz . Gəlin birbaşa mətləbə keçək. Başlamağa kömək etmək üçün mövcud statik metodların siyahısına nəzər salaq:
Onlardan istifadə üçün seçimlər bunlardır:
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
Bu kodu işlətsək, yaradılışın bütün zəncirin başlamasını nəzərdə tutduğunu görərik . Buna görə də, Java8-dən SteamAPI ilə müəyyən oxşarlıq olsa da, bu yanaşmalar arasındakı fərq budur. Misal üçün:
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-nin bir nümunəsidir (bu barədə ətraflı burada oxuya bilərsiniz "
Şəkillər və Nümunələrdəki Java 8 Stream API Bələdçisi "). Bu kodu işlətsəniz, görünməyəcək
Executed
. Yəni Java-da axın yaratarkən axın dərhal başlamır, ondan dəyər lazım olana qədər gözləyir. Ancaq
CompletableFuture
hesablanmış dəyərin soruşulmasını gözləmədən dərhal icra üçün zənciri işə salır. Məncə bunu başa düşmək vacibdir. Beləliklə, bizdə CompletableFuture var. Necə bir zəncir yarada bilərik və hansı vasitələrimiz var? Əvvəllər haqqında yazdığımız funksional interfeysləri xatırlayaq.
- A qəbul edən və B qaytaran funksiyamız (
Function
) var. Onun tək metodu var - apply
(tətbiq et).
Consumer
Bizdə A qəbul edən və heç nə qaytarmayan ( Void ) istehlakçı ( ) var . Onun yalnız bir üsulu var - accept
(qəbul edin).
Runnable
Qəbul etməyən və ya geri dönməyən bir mövzuda işləyən kodumuz var . Onun tək bir üsulu var - run
(çalışdır).
Xatırlamaq lazım olan ikinci şey,
CompletalbeFuture
işində
Runnable
istehlakçılardan və funksiyalardan istifadə etməsidir. Bunu nəzərə alaraq, həmişə bunu edə biləcəyinizi xatırlaya bilərsiniz
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);
}
Metodların versiyaları
thenRun
var . _ Bu o deməkdir ki, bu mərhələlər yeni mövzuda icra olunacaq. Xüsusi hovuzdan götürüləcək, ona görə də onun hansı axın olacağı əvvəlcədən bilinmir, yeni və ya köhnə. Hamısı tapşırıqların nə qədər çətin olduğundan asılıdır. Bu üsullara əlavə olaraq, daha üç maraqlı imkan var. Aydınlıq üçün təsəvvür edək ki, haradansa mesaj alan müəyyən bir xidmətimiz var və bunun üçün vaxt lazımdır:
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);
}
}
}
İndi isə gəlin digər xüsusiyyətlərə baxaq
CompletableFuture
. Nəticəni
CompletableFuture
başqasının nəticəsi ilə birləşdirə bilərik
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();
Qeyd etmək lazımdır ki, standart olaraq mövzular daemon ipləri olacaq, buna görə də aydınlıq üçün
get
nəticəni gözləmək üçün istifadə edirik. Və biz nəinki birləşdirə bilərik (birləşdirə bilərik), həm də geri qaytara bilərik
CompletableFuture
:
CompletableFuture.completedFuture(2L)
.thenCompose((val) -> CompletableFuture.completedFuture(val + 2))
.thenAccept(result -> System.out.println(result));
Burada qeyd etmək istərdim ki, qısalıq üçün metoddan istifadə edilmişdir
CompletableFuture.completedFuture
. Bu üsul yeni bir ip yaratmır, buna görə də zəncirin qalan hissəsi adlandırıldığı eyni ipdə yerinə yetiriləcəkdir
completedFuture
. Bir üsul da var
thenAcceptBoth
. -a çox bənzəyir
accept
, lakin
thenAccept
qəbul edirsə
consumer
, o zaman giriş kimi başqa bir +
thenAcceptBoth
qəbul edir , yəni bir deyil, 2 mənbəni giriş kimi qəbul edir. Sözlə başqa bir maraqlı ehtimal da var : Bu üsullar alternativi qəbul edir və birinci icra olunanda icra olunacaq . Mən bu araşdırmanı başqa bir maraqlı xüsusiyyətlə - səhvlərin idarə edilməsi ilə bitirmək istərdim .
CompletableStage
BiConsumer
consumer
Either
CompletableStage
CompletableStage
CompletableFuture
CompletableFuture.completedFuture(2L)
.thenApply((a) -> {
throw new IllegalStateException("error");
}).thenApply((a) -> 3L)
.thenAccept(val -> System.out.println(val));
Bu kod heç nə etməyəcək, çünki... istisna atılacaq və heç nə olmayacaq. Ancaq şərhi ləğv etsək
exceptionally
, davranışı müəyyənləşdiririk. Bu mövzuda
CompletableFuture
aşağıdakı videoya baxmağı da tövsiyə edirəm :
Mənim təvazökar fikrimcə, bu videolar İnternetdəki ən vizual videolardan biridir. Onlardan hər şeyin necə işlədiyi, hansı arsenalımız olduğu və bunun nə üçün lazım olduğu aydın olmalıdır.
Nəticə
Ümid edirik ki, indi aydındır ki, mövzular hesablandıqdan sonra hesablamaları əldə etmək üçün necə istifadə oluna bilər. Əlavə material:
#Viaçeslav
GO TO FULL VERSION