CodeGym /Blog Jawa /Acak /Luwih apik bebarengan: Jawa lan kelas Utas. Part IV - Cal...
John Squirrels
tingkat
San Francisco

Luwih apik bebarengan: Jawa lan kelas Utas. Part IV - Callable, Future, lan kanca-kanca

Diterbitake ing grup

Pambuka

Ing Part I , kita nliti carane Utas digawe. Ayo kelingan sepisan maneh. Luwih apik bebarengan: Jawa lan kelas Utas.  Part IV - Bisa Ditelpon, Masa Depan, lan kanca - 1Utas 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-catchpamblokiran 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 getcara 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 monitorutawa wait, nanging minangka cara menowo park()(amarga LockSupportmekanisme 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 Supplierpersediaan. Wis ora paramèter, nanging ngasilake soko. Iki carane nyedhiyakake barang. A Consumernganggo. 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 CompletableFuturemuncul ing Java 1.8. Iku ngleksanakake Futureantarmuka, IE tugas kita bakal rampung ing mangsa, lan kita bisa nelpon get()kanggo njaluk asil. Nanging uga ngleksanakake CompletionStageantarmuka. 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: Luwih apik bebarengan: Jawa lan kelas Utas.  Part IV - Bisa Ditelpon, Masa Depan, lan kanca - 2Ing 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 CompletableFutureuga 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 CompletableFuturewiwit 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 Functionsing njupuk A lan ngasilake B. Wis cara siji apply():.
  • Kita duwe Consumersing 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 CompletableFuturenggunakake Runnable, Consumers, lan Functionsing 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 CompletableFuturenyedhiyakake. Kita bisa gabungke asil a CompletableFuturekaro 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 completedFutureiki disebut. Ana uga thenAcceptBoth()cara. Iku banget padha accept(), nanging yen thenAccept()nampa a Consumer, thenAcceptBoth()nampa liyane CompletableStage+ BiConsumerminangka input, IE a consumersing njupuk 2 sumber tinimbang siji. Ana kemampuan menarik liyane sing ditawakake dening metode sing jenenge kalebu tembung "Salah": Luwih apik bebarengan: Jawa lan kelas Utas.  Bagian IV - Bisa Ditelpon, Masa Depan, lan kanca - 3Cara iki nampa alternatif CompletableStagelan dieksekusi ing CompletableStagesing 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!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION