تعارف
ہم پہلے ہی دیکھ چکے ہیں کہ
پہلے حصے میں تھریڈز کیسے بنتے ہیں ۔ چلو پھر یاد کرتے ہیں۔
تھریڈ ایک
Thread
ایسی چیز ہے جو اس میں چلتی ہے
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 کا ایک بھائی ہے اور اس کا نام
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
. آپ اسے دھاگوں میں کاموں کے ساتھ کام کرنے کے پرانے ماڈل کے ایک قسم کے اڈاپٹر اور نئے ماڈل پر غور کر سکتے ہیں (اس لحاظ سے کہ یہ جاوا 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 میں
WAITING کو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
--.فراہم کرنے والا. اس کے کوئی پیرامیٹرز نہیں ہیں، لیکن یہ کچھ واپس کرتا ہے، یعنی یہ اسے فراہم کرتا ہے۔
Consumer
- صارف یہ ان پٹ (پیرامیٹر s) کے طور پر کچھ لیتا ہے اور اس کے ساتھ کچھ کرتا ہے، یعنی کچھ کھاتا ہے۔ ایک اور فنکشن ہے۔ یہ کچھ لیتا ہے بطور input (پیرامیٹر
s
)، کچھ کرتا ہے اور کچھ لوٹاتا ہے۔ جیسا کہ ہم دیکھتے ہیں، جنرک فعال طور پر استعمال ہوتے ہیں۔ اگر آپ کو یقین نہیں ہے، تو آپ انہیں یاد کر سکتے ہیں اور "
جاوا میں جنرکس کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے " پڑھ سکتے ہیں۔
مکمل مستقبل
جیسے جیسے وقت گزرتا گیا، جاوا 1.8 نے ایک نئی کلاس متعارف کرائی جس کا نام ہے
CompletableFuture
۔ یہ انٹرفیس کو لاگو کرتا ہے
Future
، اس کا مطلب ہے کہ ہمارا
task
مستقبل میں عمل درآمد کیا جائے گا اور ہم اس پر عمل درآمد کر سکتے ہیں
get
اور نتیجہ حاصل کر سکتے ہیں۔ لیکن وہ کچھ کو لاگو بھی کرتا ہے
CompletionStage
۔ ترجمہ سے اس کا مقصد پہلے ہی واضح ہے: یہ کسی قسم کے حساب کا ایک خاص مرحلہ ہے۔ موضوع کا ایک مختصر تعارف جائزہ "
تعارف تکمیل کے مرحلے اور مکمل مستقبل " میں پایا جا سکتا ہے۔ آئیے سیدھے بات کی طرف آتے ہیں۔ آئیے شروع کرنے میں ہماری مدد کے لیے دستیاب جامد طریقوں کی فہرست دیکھیں:
ان کو استعمال کرنے کے اختیارات یہ ہیں:
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
پوری سلسلہ شروع کرنا شامل ہے۔ لہذا، جبکہ Java8 سے SteamAPI کے ساتھ کچھ مماثلت ہے، یہ ان طریقوں کے درمیان فرق ہے۔ مثال کے طور پر:
List<String> array = Arrays.asList("one", "two");
Stream<String> stringStream = array.stream().map(value -> {
System.out.println("Executed");
return value.toUpperCase();
});
یہ جاوا 8 اسٹریم اے پی آئی کی ایک مثال ہے (آپ اس کے بارے میں مزید یہاں پڑھ سکتے ہیں "
تصاویر اور مثالوں میں جاوا 8 اسٹریم API گائیڈ ")۔ اگر آپ یہ کوڈ چلاتے ہیں تو یہ
Executed
ظاہر نہیں ہوگا۔ یعنی جاوا میں سٹریم بناتے وقت یہ سلسلہ فوراً شروع نہیں ہوتا بلکہ اس وقت تک انتظار کرتا ہے جب تک کہ اس سے کسی قدر کی ضرورت نہ ہو۔ لیکن
CompletableFuture
یہ حسابی قیمت کے پوچھے جانے کا انتظار کیے بغیر، فوری طور پر عملدرآمد کے لیے سلسلہ شروع کر دیتا ہے۔ میرے خیال میں یہ سمجھنا ضروری ہے۔ تو ہمارے پاس Completable Future ہے۔ ہم ایک سلسلہ کیسے بنا سکتے ہیں اور ہمارے پاس کیا ذرائع ہیں؟ آئیے ان فنکشنل انٹرفیس کے بارے میں یاد رکھیں جن کے بارے میں ہم نے پہلے لکھا تھا۔
- ہمارے پاس ایک فنکشن (
Function
) ہے جو A لیتا ہے اور B لوٹاتا ہے۔ اس کا ایک طریقہ ہے - apply
(درخواست)۔
- ہمارے پاس ایک صارف (
Consumer
) ہے جو A کو قبول کرتا ہے اور کچھ بھی واپس نہیں کرتا ( void )۔ اس کا صرف ایک طریقہ ہے - accept
(قبول)۔
- ہمارے پاس ایک دھاگے پر چلنے والا کوڈ ہے
Runnable
جو قبول یا واپس نہیں کرتا ہے۔ اس کا ایک ہی طریقہ ہے - 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);
}
طریقوں کے ورژن
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
+ کو بطور input قبول کرتا ہے
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));
یہ کوڈ کچھ نہیں کرے گا، کیونکہ... ایک استثناء پھینک دیا جائے گا اور کچھ نہیں ہوگا. لیکن اگر ہم غیر تبصرہ کرتے ہیں
exceptionally
، تو ہم طرز عمل کی وضاحت کرتے ہیں۔ میں اس موضوع پر
CompletableFuture
درج ذیل ویڈیو دیکھنے کی بھی سفارش کرتا ہوں :
میری عاجزانہ رائے میں، یہ ویڈیوز انٹرنیٹ پر سب سے زیادہ بصری ہیں۔ ان سے یہ واضح ہونا چاہیے کہ یہ سب کیسے کام کرتا ہے، ہمارے پاس کون سا ہتھیار ہے اور اس کی ضرورت کیوں ہے۔
نتیجہ
امید ہے کہ اب یہ واضح ہو گیا ہے کہ دھاگوں کا حساب لگانے کے بعد انہیں دوبارہ حاصل کرنے کے لیے کیسے استعمال کیا جا سکتا ہے۔ اضافی مواد:
#ویاچسلاو
GO TO FULL VERSION