Киришүү
Биз биринчи бөлүктө жиптер кантип түзүлөөрүн карап чыктык . Дагы бир жолу эстейли.
Жип - бул
Thread
анда иштеген нерсе , андыктан
tutorialspoint java онлайн компиляторунrun
колдонуп , төмөнкү codeду аткаралы:
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дин бир тууганы бар жана анын аты
java.util.concurrent.Callable жана ал Java 1.5те төрөлгөн. Кандай айырмачылыктар бар? Эгерде биз бул интерфейстин JavaDoc'ун жакшыраак карап көрсөк,
Runnable
жаңы интерфейстен айырмаланып,
call
натыйжаны кайтаруучу ыкманы жарыялаганын көрөбүз. Ошондой эле, демейки боюнча, ал Exception ыргытат.
try-catch
Башкача айтканда, бул бизди текшерилген өзгөчөлүктөр үчүн блокторду жазуу зарылдыгынан куткарат . Жаман эмес, туурабы?
Runnable
Эми анын ордуна жаңы тапшырмабыз бар :
Callable task = () -> {
return "Hello, World!";
};
Бирок аны менен эмне кылуу керек? Эмне үчүн бизге натыйжа берген жипте иштеген тапшырма керек? Албетте, келечекте биз келечекте аткарыла турган иш-аракеттердин натыйжасын күтөбүз. Англисче Future - Future. Жана дал ушундай аталыштагы интерфейс бар:
java.util.concurrent.Future
java.util.concurrent.Future
java.util.concurrent.Future интерфейси келечекте биз жыйынтыктарын алууну пландаштырган тапшырмалар менен иштөө үчүн APIди сүрөттөйт: натыйжаларды алуу ыкмалары, статусту текшерүү ыкмалары. Биз
Future
анын аткарылышына кызыкдар
java.util.concurrent.FutureTask . Бул эмне
Task
, эмне болот
Future
. Бул ишке ашыруунун дагы кызыктуусу, ал ишке ашырат жана
Runnable
. Сиз муну жиптердеги тапшырмалар менен иштөөнүн эски моделинин жана жаңы моделдин адаптеринин бир түрү деп эсептей аласыз (жаңы, ал java 1.5те пайда болгон деген мааниде). Бул жерде бир мисал:
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
маселенин жыйынтыгын алабыз
task
.
(!) Маанилүү, учурда натыйжа ыкманы колдонуу менен алынган,
get
аткаруу синхрондуу болуп калат. Бул жерде кандай механизм колдонулат деп ойлойсуз? Туура, синхрондоштуруу блогу жок - ошондуктан биз JVisualVMде
WAITINGmonitor
дегенди же катары эмес
wait
, ошол эле сыяктуу көрөбүз
park
(себеби механизм колдонулат
LockSupport
).
Функционалдык интерфейстер
Эми биз Java 1.8 класстары жөнүндө сүйлөшөбүз, андыктан кыскача киришүү жасоо пайдалуу болмок. Келгиле, төмөнкү codeду карап көрөлү:
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);
}
};
Керексиз codeдор көп, туурабы? Жарыяланган класстардын ар бири бир функцияны аткарат, бирок аны сүрөттөө үчүн биз бир топ керексиз жардамчы codeду колдонобуз. Жана Java иштеп чыгуучулары да ушундай ойлошкон. Ошондуктан, алар "функционалдык интерфейстердин" топтомун киргизишти (
@FunctionalInterface
) жана азыр Java өзү биз үчүн бардыгын "ойлонуп чыгат" деп чечишти, бирок маанилүүлөрүнөн башкасы:
Supplier<String> supplier = () -> "String";
Consumer<String> consumer = s -> System.out.println(s);
Function<String, Integer> converter = s -> Integer.valueOf(s);
Supplier
- камсыздоочу. Анын эч кандай параметрлери жок, бирок ал бир нерсени кайтарат, башкача айтканда, аны камсыз кылат.
Consumer
- керектөөчү. Ал киргизүү катары бир нерсени алат (параметр s) жана аны менен бир нерсе кылат, башкача айтканда, бир нерсени керектейт. Дагы бир функция бар. Ал киргизүү катары бир нерсени алат (параметр
s
), бир нерсе кылат жана бир нерсени кайтарат. Көрүнүп тургандай, генериктер активдүү колдонулат. Эгер ишенбесеңиз, аларды эстеп, "
Java'дагы генериктердин теориясы же кашааларды практикада кантип коюу керек " дегенди окуй аласыз.
CompletableFuture
Убакыттын өтүшү менен Java 1.8 жаңы классты киргизди
CompletableFuture
. Ал интерфейсти ишке ашырат
Future
, демек биздики
task
келечекте аткарылат жана биз аткарып
get
, натыйжага жетише алабыз. Бирок ал да кээ бир ишке ашырат
CompletionStage
. Котормодон анын максаты айкын көрүнүп турат: бул кандайдыр бир эсептөөнүн белгилүү бир стадиясы. Темага кыскача киришүүнү "
ComletionStage жана CompletableFuture " менен таанышууга болот. Келгиле, түз эле сөзгө келели. Баштоо үчүн колдо болгон статикалык ыкмалардын тизмесин карап көрөлү:
Бул жерде аларды колдонуунун варианттары:
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
Эгерде биз бул codeду иштетсек, анда биз түзүү бүт чынжырды баштоону талап кылаарын көрөбүз . Ошондуктан, Java8ден SteamAPI менен кандайдыр бир окшоштуктар болсо да, бул ыкмалардын ортосундагы айырма. Мисалы:
List<String> array = Arrays.asList("one", "two");
Stream<String> stringStream = array.stream().map(value -> {
System.out.println("Executed");
return value.toUpperCase();
});
Бул Java 8 Stream Api үлгүсү (бул тууралуу кененирээк бул жерден окуй аласыз "
Java 8 Stream API колдонмосу сүрөттөрдө жана мисалдарда "). Бул codeду иштетсеңиз, ал
Executed
көрсөтүлбөйт. Башкача айтканда, Java'да агым түзүүдө агым дароо башталbyte, бирок андан маани керек болгонго чейин күтөт. Бирок
CompletableFuture
ал эсептелген нарктын суралышын күтпөстөн, дароо аткаруу үчүн чынжырды баштайт. Мен муну түшүнүү маанилүү деп ойлойм. Ошентип, бизде CompletableFuture бар. Кантип чынжырды түзө алабыз жана бизде кандай каражаттар бар? Биз буга чейин жазган функционалдык интерфейстерди эстейли.
- Бизде ( ) функциясы бар
Function
, ал А ны алып, B кайтарат. Анын бир гана ыкмасы бар - apply
(колдонуу).
- Бизде керектөөчүбүз (
Consumer
) А кабыл алып, эч нерсе кайтарbyte ( Void ). Анын бир гана ыкмасы бар - accept
(кабыл алуу).
Runnable
Бизде кабыл алынбаган же кайтарылбаган жипте иштеп жаткан code бар . Анын бир ыкмасы бар - run
(чуркоо).
Эсте турган экинчи нерсе,
CompletalbeFuture
өз ишинде ал
Runnable
керектөөчүлөрдү жана функцияларды колдонот.
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);
}
Методдордун versionлары
thenRun
бар . _ Бул бул этаптар жаңы жипте аткарылат дегенди билдирет. Атайын көлмөдөн алынат, андыктан жаңыбы, эскиби, кандай агым болору алдын ала белгисиз. Мунун баары милдеттердин канчалык татаал экендигинен көз каранды. Бул ыкмалардан тышкары дагы үч кызыктуу мүмкүнчүлүктөр бар. Түшүнүктүү болуу үчүн, бизде кайсы бир жерден кабар алган белгилүү бир кызмат бар экенин элестетип көрөлү жана ал убакытты талап кылат:
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);
}
}
}
Эми башка өзгөчөлүктөрүн карап көрөлү
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
биз натыйжаны күтүүгө колдонобуз. Жана биз айкалыштыруу (айкалыштыруу) гана эмес, ошондой эле кайтара алабыз
CompletableFuture
:
CompletableFuture.completedFuture(2L)
.thenCompose((val) -> CompletableFuture.completedFuture(val + 2))
.thenAccept(result -> System.out.println(result));
Бул жерде мен кыска үчүн, ыкма колдонулганын белгилегим келет
CompletableFuture.completedFuture
. Бул ыкма жаңы жипти түзбөйт, андыктан чынжырдын калган бөлүгү ал чакырылган жипте аткарылат
completedFuture
. Ошондой эле бир ыкмасы бар
thenAcceptBoth
. Ал абдан окшош
accept
, бирок
thenAccept
кабыл алса
consumer
, анда
thenAcceptBoth
ал башка
CompletableStage
+ киргизүү катары кабыл алат
BiConsumer
, башкача айтканда
consumer
, бир эмес, 2 булакты киргизүү катары кабыл алат. Сөз менен дагы бир кызыктуу мүмкүнчүлүк бар
Either
:
Бул ыкмалар альтернативаны кабыл алат жана биринчи аткарылганга
CompletableStage
аткарылат .
CompletableStage
Жана мен бул кароону дагы бир кызыктуу өзгөчөлүк менен бүтүргүм келет
CompletableFuture
- каталарды иштетүү.
CompletableFuture.completedFuture(2L)
.thenApply((a) -> {
throw new IllegalStateException("error");
}).thenApply((a) -> 3L)
.thenAccept(val -> System.out.println(val));
Бул code эч нерсе кылbyte, анткени... бир өзгөчөлүк ыргытылат жана эч нерсе болбойт. Бирок биз комментарийден баш тартсак
exceptionally
, анда жүрүм-турумду аныктайбыз. Мен дагы ушул тема боюнча
CompletableFuture
төмөнкү видеону көрүүнү сунуштайм :
Менин оюмча, бул видеолор интернеттеги эң визуалдык видеолордун бири. Алардан мунун баары кантип иштээри, бизде кандай арсенал бар жана мунун баары эмне үчүн керек экени түшүнүктүү болушу керек.
Корутунду
Эми жиптер эсептелгенден кийин эсептөөлөрдү алуу үчүн кантип колдонсо болору айкын болду деп үмүттөнөбүз. Кошумча материал:
#Вячеслав
GO TO FULL VERSION