Pambuka
Ing
Part I , kita nliti carane Utas digawe. Ayo kelingan sepisan maneh.
Utas diwakili dening kelas Utas, sing
run()
metode kasebut diarani. Dadi ayo gunakake
kompiler Java online Tutorialspoint lan nglakokake kode ing ngisor iki:
public class HelloWorld {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println("Hello World");
};
new Thread(task).start();
}
}
Apa iki mung pilihan kanggo miwiti tugas ing thread?
java.util.concurrent.Callable
Pranyata
java.lang.Runnable duwe sedulur jenenge
java.util.concurrent.Callable sing teka ing donya ing Jawa 1.5. Apa bedane? Yen sampeyan ndeleng kanthi cetha ing Javadoc kanggo antarmuka iki, kita weruh yen, ora kaya
Runnable
, antarmuka anyar nyatakake
call()
metode sing ngasilake asil. Uga, mbalang Exception minangka standar. Sing, iku nyimpen kita saka kudu
try-catch
pamblokiran kanggo dicenthang pangecualian. Ora ala, ta? Saiki kita duwe tugas anyar tinimbang
Runnable
:
Callable task = () -> {
return "Hello, World!";
};
Nanging apa sing kudu ditindakake? Napa kita butuh tugas sing mlaku ing thread sing ngasilake asil? Temenan, kanggo tumindak apa wae sing ditindakake ing mangsa ngarep, kita ngarepake bakal nampa asil saka tumindak kasebut ing mangsa ngarep. Lan kita duwe antarmuka kanthi jeneng sing cocog:
java.util.concurrent.Future
java.util.concurrent.Future
Antarmuka
java.util.concurrent.Future nemtokake API kanggo nggarap tugas sing asile bakal ditampa ing mangsa ngarep: cara kanggo entuk asil, lan cara kanggo mriksa status. Ing babagan
Future
, kita kasengsem ing implementasine ing kelas
java.util.concurrent.FutureTask . Iki minangka "Tugas" sing bakal ditindakake ing
Future
. Sing nggawe implementasine iki luwih menarik yaiku uga ngleksanakake Runnable. Sampeyan bisa nimbang iki jenis adaptor antarane model lawas nggarap tugas ing Utas lan model anyar (anyar ing pangertèn sing katon ing Jawa 1.5). Punika conto:
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());
}
}
Minangka sampeyan bisa ndeleng saka conto, kita nggunakake
get
cara kanggo entuk asil saka tugas.
Cathetan:yen sampeyan entuk asil nggunakake
get()
metode kasebut, eksekusi dadi sinkron! Mekanisme apa sampeyan mikir bakal digunakake ing kene? Bener, ora ana blok sinkronisasi. Pramila kita ora bakal weruh
WAITING ing JVisualVM minangka
monitor
utawa
wait
, nanging minangka cara menowo
park()
(amarga
LockSupport
mekanisme digunakake).
Antarmuka fungsional
Sabanjure, kita bakal ngomong babagan kelas saka Java 1.8, mula kita bakal menehi pambuka ringkes. Deleng kode ing ngisor iki:
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);
}
};
Akeh lan akeh kode ekstra, apa sampeyan ora bakal ngomong? Saben kelas ngumumaké nindakake siji fungsi, nanging kita nggunakake Bunch saka kode ndhukung ekstra kanggo netepake. Lan iki carane pangembang Jawa panginten. Mulane, dheweke ngenalake sakumpulan "antarmuka fungsional" (
@FunctionalInterface
) lan mutusake yen saiki Jawa dhewe bakal nindakake "mikir", mung ninggalake sing penting kanggo kita kuwatir:
Supplier<String> supplier = () -> "String";
Consumer<String> consumer = s -> System.out.println(s);
Function<String, Integer> converter = s -> Integer.valueOf(s);
A
Supplier
persediaan. Wis ora paramèter, nanging ngasilake soko. Iki carane nyedhiyakake barang. A
Consumer
nganggo. Butuh soko minangka input (argumentasi) lan nindakake soko karo. Argumentasi yaiku apa sing dikonsumsi. Banjur kita uga duwe
Function
. Butuh input (argumen), nindakake soko, lan ngasilake soko. Sampeyan bisa ndeleng manawa kita aktif nggunakake obat generik. Yen sampeyan ora yakin, sampeyan bisa njaluk refresher kanthi maca "
Generics in Java: how to use angled brackets in practice ".
CompletableFuture
Wektu liwati lan kelas anyar sing diarani
CompletableFuture
muncul ing Java 1.8. Iku ngleksanakake
Future
antarmuka, IE tugas kita bakal rampung ing mangsa, lan kita bisa nelpon
get()
kanggo njaluk asil. Nanging uga ngleksanakake
CompletionStage
antarmuka. Jeneng kasebut kabeh: iki minangka tataran tartamtu saka sawetara pitungan. Pambuka ringkes babagan topik kasebut bisa ditemokake ing review ing kene: Pambuka kanggo CompletionStage lan CompletableFuture. Ayo langsung menyang titik. Ayo goleki dhaptar metode statis sing kasedhiya sing bakal mbantu kita miwiti:
Ing ngisor iki pilihan kanggo nggunakake:
import java.util.concurrent.CompletableFuture;
public class App {
public static void main(String[] args) throws Exception {
// A CompletableFuture that already contains a Result
CompletableFuture<String> completed;
completed = CompletableFuture.completedFuture("Just a value");
// A CompletableFuture that runs a new thread from Runnable. That's why it's Void
CompletableFuture<Void> voidCompletableFuture;
voidCompletableFuture = CompletableFuture.runAsync(() -> {
System.out.println("run " + Thread.currentThread().getName());
});
// A CompletableFuture that starts a new thread whose result we'll get from a Supplier
CompletableFuture<String> supplier;
supplier = CompletableFuture.supplyAsync(() -> {
System.out.println("supply " + Thread.currentThread().getName());
return "Value";
});
}
}
Yen kita nglakokaké kode iki, kita bakal weruh sing nggawe
CompletableFuture
uga melu miwiti pipeline kabèh. Mulane, kanthi kamiripan tartamtu karo SteamAPI saka Java8, ing kene kita nemokake prabédan antarane pendekatan kasebut. Tuladhane:
List<String> array = Arrays.asList("one", "two");
Stream<String> stringStream = array.stream().map(value -> {
System.out.println("Executed");
return value.toUpperCase();
});
Iki minangka conto Java 8's Stream API. Yen sampeyan mbukak kode iki, sampeyan bakal weruh yen "Dieksekusi" ora bakal ditampilake. Ing tembung liyane, nalika stream digawe ing Jawa, stream ora langsung diwiwiti. Nanging, ngenteni wong sing pengin nilai saka iku. Nanging
CompletableFuture
wiwit nglakokaké pipo langsung, tanpa ngenteni wong takon kanggo Nilai. Aku iki penting kanggo ngerti. S o, kita duwe
CompletableFuture
. Kepiye carane nggawe pipa (utawa chain) lan mekanisme apa sing kita duwe? Elinga antarmuka fungsional sing wis ditulis sadurunge.
- We duwe
Function
sing njupuk A lan ngasilake B. Wis cara siji apply()
:.
- Kita duwe
Consumer
sing njupuk A lan ora ngasilake apa-apa (Void). Wis siji cara: accept()
.
- We have
Runnable
, kang mlaku ing Utas, lan njupuk apa-apa lan bali apa-apa. Wis siji cara: run()
.
Ing bab sabanjure kanggo elinga iku
CompletableFuture
nggunakake
Runnable
,
Consumers
, lan
Functions
ing sawijining karya. Dadi, sampeyan bisa ngerti manawa sampeyan bisa nindakake ing ngisor iki kanthi
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);
}
, , lan metode duwe
thenRun()
versi "Async". Iki tegese tahapan kasebut bakal rampung ing benang sing beda. Utas iki bakal dijupuk saka blumbang khusus — dadi kita ora bakal ngerti luwih dhisik apa bakal dadi utas anyar utawa lawas. Iku kabeh gumantung carane komputasi-intensif tugas. Saliyane cara kasebut, ana telung kemungkinan sing luwih menarik. Kanggo gamblang, ayo bayangake yen kita duwe layanan tartamtu sing nampa sawetara jinis pesen saka ngendi wae - lan iki mbutuhake wektu:
thenApply()
thenAccept()
public static class NewsService {
public static String getMessage() {
try {
Thread.currentThread().sleep(3000);
return "Message";
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
Saiki, ayo goleki kabisan liyane sing
CompletableFuture
nyedhiyakake. Kita bisa gabungke asil a
CompletableFuture
karo asil liyane
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();
Elinga yen utas minangka utas daemon kanthi standar, supaya luwih jelas, kita kudu
get()
ngenteni asile. Ora mung kita bisa gabungke
CompletableFutures
, kita uga bisa ngasilake
CompletableFuture
:
CompletableFuture.completedFuture(2L)
.thenCompose((val) -> CompletableFuture.completedFuture(val + 2))
.thenAccept(result -> System.out.println(result));
Kene aku pengin Wigati sing
CompletableFuture.completedFuture()
cara iki digunakake kanggo brevity. Cara iki ora nggawe thread anyar, supaya liyane saka pipeline bakal kaleksanan ing thread padha ngendi
completedFuture
iki disebut. Ana uga
thenAcceptBoth()
cara. Iku banget padha
accept()
, nanging yen
thenAccept()
nampa a
Consumer
,
thenAcceptBoth()
nampa liyane
CompletableStage
+
BiConsumer
minangka input, IE a
consumer
sing njupuk 2 sumber tinimbang siji. Ana kemampuan menarik liyane sing ditawakake dening metode sing jenenge kalebu tembung "Salah":
Cara iki nampa alternatif
CompletableStage
lan dieksekusi ing
CompletableStage
sing bakal dieksekusi luwih dhisik. Pungkasan, aku pengin mungkasi review iki kanthi fitur menarik liyane
CompletableFuture
: penanganan kesalahan.
CompletableFuture.completedFuture(2L)
.thenApply((a) -> {
throw new IllegalStateException("error");
}).thenApply((a) -> 3L)
//.exceptionally(ex -> 0L)
.thenAccept(val -> System.out.println(val));
Kode iki ora bakal nindakake apa-apa, amarga bakal ana pangecualian lan ora ana sing bakal kelakon. Nanging kanthi uncommenting statement "luar biasa", kita nemtokake prilaku samesthine. Ngomong babagan
CompletableFuture
, aku uga menehi saran supaya sampeyan nonton video ing ngisor iki:
Miturut pendapatku, iki minangka salah sawijining video sing paling panjelasan ing Internet. Dheweke kudu nerangake carane kabeh iki bisa digunakake, toolkit apa sing kasedhiya, lan kenapa kabeh iki dibutuhake.
Kesimpulan
Muga-muga, saiki wis jelas kepiye sampeyan bisa nggunakake utas kanggo ngetung sawise rampung. Materi tambahan:
Luwih apik bebarengan: Jawa lan kelas Utas. Bagian I - Utas eksekusi Luwih apik bebarengan: Jawa lan kelas Utas. Part II - Sinkronisasi Luwih apik bebarengan: Jawa lan kelas Utas. Bagean III - Interaksi Better bebarengan: Jawa lan kelas Utas. Part V - Executor, ThreadPool, Fork / Gabung luwih apik bebarengan: Jawa lan kelas Utas. Bab VI - Mbusak!
GO TO FULL VERSION