పరిచయం
పార్ట్ I లో , థ్రెడ్లు ఎలా సృష్టించబడతాయో మేము సమీక్షించాము. మరొక్కసారి గుర్తుచేసుకుందాం.
థ్రెడ్ థ్రెడ్ క్లాస్ ద్వారా సూచించబడుతుంది, దీని
run()
పద్ధతిని పిలుస్తారు.
కాబట్టి ట్యుటోరియల్స్పాయింట్ ఆన్లైన్ జావా కంపైలర్ని ఉపయోగిస్తాము మరియు క్రింది కోడ్ను అమలు చేయండి:
public class HelloWorld {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println("Hello World");
};
new Thread(task).start();
}
}
థ్రెడ్లో టాస్క్ను ప్రారంభించడానికి ఇది ఏకైక ఎంపికనా?
java.util.concurrent.Callable
java.lang.Runnableకి జావా 1.5లో ప్రపంచంలోకి వచ్చిన
java.util.concurrent.Callable అనే సోదరుడు ఉన్నట్లు తేలింది . తేడాలు ఏమిటి? మీరు ఈ ఇంటర్ఫేస్ కోసం జావాడోక్ని నిశితంగా పరిశీలిస్తే,
Runnable
కొత్త ఇంటర్ఫేస్
call()
ఫలితాన్ని ఇచ్చే పద్ధతిని డిక్లేర్ చేస్తుంది. అలాగే, ఇది డిఫాల్ట్గా మినహాయింపును విసురుతుంది. అంటే,
try-catch
తనిఖీ చేయబడిన మినహాయింపుల కోసం బ్లాక్ చేయకుండా ఇది మనల్ని కాపాడుతుంది. చెడ్డది కాదు, సరియైనదా? ఇప్పుడు మనకు బదులుగా కొత్త పని ఉంది
Runnable
:
Callable task = () -> {
return "Hello, World!";
};
కానీ దానితో మనం ఏమి చేస్తాము? ఫలితాన్ని అందించే థ్రెడ్పై నడుస్తున్న పని మనకు ఎందుకు అవసరం? సహజంగానే, భవిష్యత్తులో చేసే ఏవైనా చర్యలకు, భవిష్యత్తులో ఆ చర్యల ఫలితాన్ని అందుకోవాలని మేము ఆశిస్తున్నాము. మరియు మేము సంబంధిత పేరుతో ఇంటర్ఫేస్ని కలిగి ఉన్నాము:
java.util.concurrent.Future
java.util.concurrent.భవిష్యత్తు
java.util.concurrent.Future ఇంటర్ఫేస్ టాస్క్లతో పని చేయడం కోసం APIని నిర్వచిస్తుంది, దీని
ఫలితాలు మేము భవిష్యత్తులో అందుకోవాలనుకుంటున్నాము: ఫలితాన్ని పొందే పద్ధతులు మరియు స్థితిని తనిఖీ చేసే పద్ధతులు. కు సంబంధించి ,
java.util.concurrent.FutureTaskFuture
క్లాస్లో దాని అమలుపై మాకు ఆసక్తి ఉంది . ఇది లో అమలు చేయబడే "టాస్క్" . ఈ అమలును మరింత ఆసక్తికరంగా చేసేది ఏమిటంటే, ఇది రన్బుల్ని కూడా అమలు చేస్తుంది. థ్రెడ్లపై టాస్క్లతో పని చేసే పాత మోడల్ మరియు కొత్త మోడల్ (జావా 1.5లో కనిపించిన అర్థంలో కొత్తది) మధ్య మీరు దీన్ని ఒక రకమైన అడాప్టర్గా పరిగణించవచ్చు. ఇక్కడ ఒక ఉదాహరణ:
Future
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());
}
}
మీరు ఉదాహరణ నుండి చూడగలిగినట్లుగా,
get
పని నుండి ఫలితాన్ని పొందడానికి మేము పద్ధతిని ఉపయోగిస్తాము.
గమనిక:మీరు పద్ధతిని ఉపయోగించి ఫలితాన్ని పొందినప్పుడు
get()
, అమలు సమకాలీకరించబడుతుంది! ఇక్కడ ఏ మెకానిజం ఉపయోగించబడుతుందని మీరు అనుకుంటున్నారు? నిజమే, సింక్రొనైజేషన్ బ్లాక్ లేదు.
అందుకే మేము JVisualVMలో వేచి ఉండడాన్ని ఒక
monitor
లేదా
wait
, తెలిసిన పద్ధతిగా చూడము
park()
(ఎందుకంటే
LockSupport
మెకానిజం ఉపయోగించబడుతోంది).
ఫంక్షనల్ ఇంటర్ఫేస్లు
తరువాత, మేము జావా 1.8 నుండి తరగతుల గురించి మాట్లాడుతాము, కాబట్టి మేము క్లుప్త పరిచయాన్ని అందించడం మంచిది. కింది కోడ్ను చూడండి:
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);
}
};
చాలా మరియు చాలా అదనపు కోడ్, మీరు చెప్పలేదా? డిక్లేర్డ్ క్లాస్లలో ప్రతి ఒక్కటి ఒక ఫంక్షన్ను నిర్వహిస్తుంది, అయితే దానిని నిర్వచించడానికి మేము అదనపు సపోర్టింగ్ కోడ్ని ఉపయోగిస్తాము. మరియు జావా డెవలపర్లు ఇలా ఆలోచించారు. తదనుగుణంగా, వారు "ఫంక్షనల్ ఇంటర్ఫేస్ల" ( ) సమితిని ప్రవేశపెట్టారు
@FunctionalInterface
మరియు ఇప్పుడు జావా కూడా "ఆలోచించడం" చేస్తుందని నిర్ణయించుకున్నారు, మనం ఆందోళన చెందడానికి ముఖ్యమైన అంశాలను మాత్రమే వదిలివేసారు:
Supplier<String> supplier = () -> "String";
Consumer<String> consumer = s -> System.out.println(s);
Function<String, Integer> converter = s -> Integer.valueOf(s);
ఒక
Supplier
సరఫరా. దీనికి పారామీటర్లు లేవు, కానీ అది ఏదో రిటర్న్ చేస్తుంది. ఇది వస్తువులను ఈ విధంగా సరఫరా చేస్తుంది. A
Consumer
వినియోగిస్తుంది. ఇది ఏదైనా ఇన్పుట్గా తీసుకుంటుంది (ఒక వాదన) మరియు దానితో ఏదైనా చేస్తుంది. అది వినియోగిస్తుంది అనేదే వాదన. అప్పుడు మనకు కూడా ఉంది
Function
. ఇది ఇన్పుట్లను (వాదనలు) తీసుకుంటుంది, ఏదైనా చేస్తుంది మరియు ఏదైనా తిరిగి ఇస్తుంది. మేము జెనరిక్స్ను చురుకుగా ఉపయోగిస్తున్నామని మీరు చూడవచ్చు. మీకు ఖచ్చితంగా తెలియకుంటే, "
జావాలో జెనరిక్స్: ఆచరణలో కోణ బ్రాకెట్లను ఎలా ఉపయోగించాలి " చదవడం ద్వారా మీరు రిఫ్రెషర్ పొందవచ్చు.
పూర్తి చేయదగిన భవిష్యత్తు
CompletableFuture
సమయం గడిచిపోయింది మరియు జావా 1.8లో ఒక కొత్త తరగతి కనిపించింది. ఇది
Future
ఇంటర్ఫేస్ను అమలు చేస్తుంది, అంటే భవిష్యత్తులో మా పనులు పూర్తవుతాయి మరియు
get()
ఫలితాన్ని పొందడానికి మేము కాల్ చేయవచ్చు. కానీ ఇది
CompletionStage
ఇంటర్ఫేస్ను కూడా అమలు చేస్తుంది. పేరు అన్నింటినీ చెబుతుంది: ఇది కొన్ని గణనల యొక్క నిర్దిష్ట దశ. అంశానికి సంబంధించిన సంక్షిప్త పరిచయాన్ని ఇక్కడ సమీక్షలో చూడవచ్చు: కంప్లీషన్స్టేజ్ మరియు కంప్లీటబుల్ ఫ్యూచర్ పరిచయం. అసలు విషయానికి వద్దాం. ప్రారంభించడానికి మాకు సహాయపడే అందుబాటులో ఉన్న స్టాటిక్ పద్ధతుల జాబితాను చూద్దాం:
వాటిని ఉపయోగించడం కోసం ఇక్కడ ఎంపికలు ఉన్నాయి:
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";
});
}
}
CompletableFuture
మేము ఈ కోడ్ని అమలు చేస్తే, మొత్తం పైప్లైన్ను ప్రారంభించడం కూడా ఒక సృష్టిని కలిగి ఉంటుందని మేము చూస్తాము . అందువల్ల, Java8 నుండి SteamAPIకి కొంత సారూప్యతతో, ఇక్కడే ఈ విధానాల మధ్య వ్యత్యాసాన్ని మనం కనుగొంటాము. ఉదాహరణకి:
List<String> array = Arrays.asList("one", "two");
Stream<String> stringStream = array.stream().map(value -> {
System.out.println("Executed");
return value.toUpperCase();
});
ఇది జావా 8 యొక్క స్ట్రీమ్ APIకి ఉదాహరణ. మీరు ఈ కోడ్ని అమలు చేస్తే, "అమలు చేయబడింది" ప్రదర్శించబడదని మీరు చూస్తారు. మరో మాటలో చెప్పాలంటే, జావాలో స్ట్రీమ్ సృష్టించబడినప్పుడు, స్ట్రీమ్ వెంటనే ప్రారంభించబడదు. బదులుగా, ఎవరైనా దాని నుండి విలువను కోరుకునే వరకు వేచి ఉంటుంది. కానీ
CompletableFuture
పైప్లైన్ను ఎవరైనా విలువ కోసం అడిగే వరకు వేచి ఉండకుండా వెంటనే అమలు చేయడం ప్రారంభిస్తుంది. ఇది అర్థం చేసుకోవడం ముఖ్యం అని నేను భావిస్తున్నాను. S o, మాకు ఒక
CompletableFuture
. మేము పైప్లైన్ (లేదా గొలుసు) ఎలా తయారు చేయవచ్చు మరియు మనకు ఏ యంత్రాంగాలు ఉన్నాయి? మేము ఇంతకు ముందు వ్రాసిన ఫంక్షనల్ ఇంటర్ఫేస్లను గుర్తుకు తెచ్చుకోండి.
- మేము
Function
Aని తీసుకొని Bని తిరిగి ఇచ్చేదాన్ని కలిగి ఉన్నాము. దీనికి ఒకే పద్ధతి ఉంది: apply()
.
- మన దగ్గర
Consumer
A తీసుకుంటుంది మరియు ఏదీ తిరిగి ఇవ్వదు (శూన్యం). దీనికి ఒకే పద్ధతి ఉంది: accept()
.
- మేము కలిగి
Runnable
, ఇది థ్రెడ్పై నడుస్తుంది మరియు ఏమీ తీసుకోదు మరియు ఏమీ తిరిగి ఇవ్వదు. దీనికి ఒకే పద్ధతి ఉంది: run()
.
గుర్తుంచుకోవలసిన తదుపరి విషయం ఏమిటంటే , , మరియు దాని పనిలో
CompletableFuture
ఉపయోగిస్తుంది . దీని ప్రకారం, మీరు ఈ క్రింది వాటిని చేయగలరని మీరు ఎల్లప్పుడూ తెలుసుకోవచ్చు :
Runnable
Consumers
Functions
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);
}
, , మరియు పద్ధతులు "Async" సంస్కరణలను కలిగి ఉన్నాయి
thenRun()
. అంటే ఈ దశలు వేరే థ్రెడ్లో పూర్తవుతాయి. ఈ థ్రెడ్ ప్రత్యేక పూల్ నుండి తీసుకోబడుతుంది — కనుక ఇది కొత్తదా లేదా పాతదా అనేది మాకు ముందుగా తెలియదు. ఇది అన్ని పనులు ఎంత గణన-ఇంటెన్సివ్ అనే దానిపై ఆధారపడి ఉంటుంది. ఈ పద్ధతులతో పాటు, మరో మూడు ఆసక్తికరమైన అవకాశాలు ఉన్నాయి. స్పష్టత కోసం, ఎక్కడి నుండైనా ఏదో ఒక రకమైన సందేశాన్ని అందుకునే నిర్దిష్ట సేవ మనకు ఉందని ఊహించుకుందాం — మరియు దీనికి సమయం పడుతుంది:
thenApply()
thenAccept()
public static class NewsService {
public static String getMessage() {
try {
Thread.currentThread().sleep(3000);
return "Message";
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
}
ఇప్పుడు, అందించే ఇతర సామర్థ్యాలను పరిశీలిద్దాం
CompletableFuture
. మేము ఒక ఫలితాన్ని
CompletableFuture
మరొక ఫలితంతో కలపవచ్చు
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();
థ్రెడ్లు డిఫాల్ట్గా డెమోన్ థ్రెడ్లు అని గమనించండి, కాబట్టి స్పష్టత కోసం, మేము
get()
ఫలితం కోసం వేచి ఉంటాము. మనం కలపడం మాత్రమే కాదు
CompletableFutures
, మనం ఒక రిటర్న్ కూడా చేయవచ్చు
CompletableFuture
:
CompletableFuture.completedFuture(2L)
.thenCompose((val) -> CompletableFuture.completedFuture(val + 2))
.thenAccept(result -> System.out.println(result));
CompletableFuture.completedFuture()
క్లుప్తత కోసం పద్ధతి ఉపయోగించబడిందని ఇక్కడ నేను గమనించాలనుకుంటున్నాను . ఈ పద్ధతి కొత్త థ్రెడ్ను సృష్టించదు, కాబట్టి మిగిలిన పైప్లైన్
completedFuture
పిలిచిన అదే థ్రెడ్లో అమలు చేయబడుతుంది. ఒక పద్ధతి కూడా ఉంది
thenAcceptBoth()
. ఇది చాలా సారూప్యంగా ఉంటుంది
accept()
, అయితే
thenAccept()
aని అంగీకరిస్తే
Consumer
, మరొక +ని ఇన్పుట్గా
thenAcceptBoth()
అంగీకరిస్తుంది , అనగా ఒక దానికి బదులుగా 2 మూలాలను తీసుకుంటుంది. "ఎయిదర్" అనే పదాన్ని కలిగి ఉన్న పద్ధతుల ద్వారా అందించే మరొక ఆసక్తికరమైన సామర్థ్యం ఉంది: ఈ పద్ధతులు ప్రత్యామ్నాయాన్ని అంగీకరిస్తాయి మరియు ముందుగా అమలు చేయబడిన వాటిపై అమలు చేయబడతాయి . చివరగా, నేను ఈ సమీక్షను మరొక ఆసక్తికరమైన ఫీచర్తో ముగించాలనుకుంటున్నాను : లోపం నిర్వహణ.
CompletableStage
BiConsumer
consumer
CompletableStage
CompletableStage
CompletableFuture
CompletableFuture.completedFuture(2L)
.thenApply((a) -> {
throw new IllegalStateException("error");
}).thenApply((a) -> 3L)
//.exceptionally(ex -> 0L)
.thenAccept(val -> System.out.println(val));
ఈ కోడ్ ఏమీ చేయదు, ఎందుకంటే మినహాయింపు ఉంటుంది మరియు ఇంకేమీ జరగదు. కానీ "అనూహ్యంగా" ప్రకటనను అన్కమెంట్ చేయడం ద్వారా, మేము ఆశించిన ప్రవర్తనను నిర్వచించాము. గురించి మాట్లాడుతూ
CompletableFuture
, ఈ క్రింది వీడియోను చూడమని కూడా నేను మీకు సిఫార్సు చేస్తున్నాను:
నా వినయపూర్వకమైన అభిప్రాయం ప్రకారం, ఇవి ఇంటర్నెట్లో అత్యంత వివరణాత్మక వీడియోలలో ఒకటి. ఇవన్నీ ఎలా పని చేస్తాయి, మనకు ఏ టూల్కిట్ అందుబాటులో ఉంది మరియు ఇవన్నీ ఎందుకు అవసరమో వారు స్పష్టంగా తెలియజేయాలి.
ముగింపు
ఆశాజనక, మీరు థ్రెడ్లు పూర్తయిన తర్వాత లెక్కలను పొందడానికి వాటిని ఎలా ఉపయోగించవచ్చో ఇప్పుడు స్పష్టంగా తెలుస్తుంది. అదనపు పదార్థం:
కలిసి ఉత్తమం: జావా మరియు థ్రెడ్ క్లాస్. పార్ట్ I — థ్రెడ్స్ ఆఫ్ ఎగ్జిక్యూషన్ కలిసి మెరుగ్గా ఉంటుంది: జావా మరియు థ్రెడ్ క్లాస్. పార్ట్ II — సమకాలీకరణ ఉత్తమం: జావా మరియు థ్రెడ్ క్లాస్. పార్ట్ III — కలిసి మెరుగ్గా పరస్పర చర్య: జావా మరియు థ్రెడ్ క్లాస్. పార్ట్ V - ఎగ్జిక్యూటర్, థ్రెడ్పూల్, ఫోర్క్/జాయిన్ బెటర్ టుగెదర్: జావా మరియు థ్రెడ్ క్లాస్. పార్ట్ VI — ఫైర్ అవే!
GO TO FULL VERSION