Java 8 এ Concurrency Enhancements (কনকারেন্সি উন্নতি) অনেক গুরুত্বপূর্ণ বৈশিষ্ট্য অন্তর্ভুক্ত করেছে যা মাল্টিথ্রেডিং এবং কনকারেন্ট প্রোগ্রামিংকে সহজ, কার্যকরী এবং নিরাপদ করে তোলে। এতে Fork/Join Framework, CompletableFuture, Streams API এবং new concurrency utilities অন্তর্ভুক্ত রয়েছে যা মাল্টিথ্রেডিং প্রোগ্রামিংয়ের ক্ষমতাকে অনেক বৃদ্ধি করেছে।
এই কনকারেন্সি উন্নতিগুলির মাধ্যমে আপনি একই সময়ে অনেক কাজ করতে পারেন (multithreading), যা পারফরম্যান্স এবং স্কেলেবিলিটি বৃদ্ধি করে।
1. Fork/Join Framework (ফর্ক/জয়েন ফ্রেমওয়ার্ক)
Java 8-এ Fork/Join Framework (ফর্ক/জয়েন ফ্রেমওয়ার্ক) ব্যবহার করার মাধ্যমে মাল্টিথ্রেডেড প্রোগ্রামিং আরও সহজ এবং কার্যকরী করা হয়েছে। এটি ডিভাইড-এন্ড-কনকার (divide-and-conquer) পদ্ধতি ব্যবহার করে বড় কাজগুলো ছোট কাজগুলোতে ভাগ করে এবং একাধিক থ্রেডে সেই কাজগুলো সম্পন্ন করে।
Fork/Join Framework-এ দুটি প্রধান অংশ রয়েছে:
- Fork: বড় কাজকে ছোট উপ-যন্ত্রণায় ভাগ করা হয়।
- Join: প্রতিটি থ্রেড তার কাজ শেষ করার পর ফলাফল একত্রিত করা হয়।
এটি মাল্টি-কোর প্রসেসরে কাজ করার জন্য কার্যকরী, কারণ এটি একাধিক থ্রেডে কাজ বিভক্ত করে দ্রুত ফলাফল পেতে সহায়ক।
উদাহরণ:
import java.util.concurrent.*;
public class ForkJoinExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ForkJoinPool pool = new ForkJoinPool();
// RecursiveTask implementation to calculate factorial
RecursiveTask<Long> task = new RecursiveTask<Long>() {
@Override
protected Long compute() {
long result = 1;
for (long i = 1; i <= 10; i++) {
result *= i;
}
return result;
}
};
Long result = pool.invoke(task);
System.out.println("Factorial Result: " + result);
}
}এখানে, ForkJoinPool ব্যবহার করা হয়েছে যাতে ফ্যাক্টরিয়াল গণনা করার কাজটি থ্রেডে ভাগ করা যায়।
2. CompletableFuture (কমপ্লিটেবলফিউচার)
CompletableFuture হল একটি নতুন কনকারেন্ট ইউটিলিটি যা মাল্টিথ্রেডিং এবং অ্যাসিনক্রোনাস প্রোগ্রামিংয়ে সহায়ক। এটি Future ইন্টারফেসের উন্নত সংস্করণ এবং এটি একটি ভাল সমাধান সরবরাহ করে যাতে আপনি অ্যাসিনক্রোনাসভাবে কাজ করতে পারেন এবং ফিউচার অবজেক্টের মান পেতে পারেন।
CompletableFuture আপনাকে চেইন করা অ্যাসিনক্রোনাস অপারেশনগুলি পরিচালনা করতে সাহায্য করে, যা কার্যকরী এবং সহজ।
উদাহরণ:
import java.util.concurrent.*;
public class CompletableFutureExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// Simulate some long running task
return 10;
});
future.thenApply(result -> result * 2) // Chaining another task
.thenAccept(finalResult -> System.out.println("Result: " + finalResult)); // Output: Result: 20
}
}এখানে, CompletableFuture ব্যবহার করা হয়েছে যা একটি অ্যাসিনক্রোনাসভাবে কাজ শুরু করে এবং পরে পরবর্তী অপারেশন চেইনিংয়ের মাধ্যমে ফলাফল প্রদান করে।
3. Streams API (স্ট্রিমস এপিআই)
Java 8-এ Streams API একাধিক থ্রেডে কাজ করার জন্য একটি গুরুত্বপূর্ণ সুবিধা প্রদান করে। parallelStream() ব্যবহার করে আপনি স্ট্রিমের ডেটা প্যারালেল প্রসেসিং করতে পারেন, যা মাল্টিকোর প্রসেসর ব্যবহারের মাধ্যমে পারফরম্যান্স বাড়ায়।
উদাহরণ:
import java.util.*;
import java.util.stream.*;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Parallel Stream
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("Sum: " + sum); // Output: Sum: 55
}
}এখানে, parallelStream() ব্যবহার করা হয়েছে যাতে স্ট্রিমের উপাদানগুলো মাল্টিপল থ্রেডে প্রসেস করা হয়, ফলে কার্যকারিতা বৃদ্ধি পায়।
4. New Concurrency Utilities (নতুন কনকারেন্সি ইউটিলিটি)
Java 8 এ নতুন কিছু কনকারেন্সি ইউটিলিটি যেমন Executors, Atomic Variables এবং CountDownLatch চালু করা হয়েছে, যা মাল্টিথ্রেডিং প্রোগ্রামিং আরও সহজ এবং কার্যকরী করেছে।
উদাহরণ (Executors):
import java.util.concurrent.*;
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Runnable task = () -> System.out.println(Thread.currentThread().getName() + " is running");
executorService.submit(task);
executorService.submit(task);
executorService.shutdown(); // Shut down the executor
}
}এখানে, ExecutorService ব্যবহার করা হয়েছে, যা থ্রেড পুল ব্যবহার করে একাধিক কাজ প্যারালেলভাবে সম্পন্ন করতে সহায়ক।
5. New ForkJoinPool and Parallel Streams
Java 8-এ ForkJoinPool এবং parallel streams এর সমন্বয়ে কার্যকরী প্যারালেল প্রসেসিং সমাধান সরবরাহ করা হয়েছে। ForkJoinPool ক্লাসের মাধ্যমে মাল্টিপল থ্রেডে একাধিক কাজকে কার্যকরভাবে ভাগ করা যায় এবং parallelStream() মেথডের মাধ্যমে ডেটা প্যারালেল প্রসেসিং করা সম্ভব হয়।
Java 8 Concurrency Enhancements এর সুবিধা
- সহজ এবং কার্যকরী মাল্টিথ্রেডিং:
- Java 8 এ ForkJoinPool, CompletableFuture, parallelStream() ইত্যাদি ব্যবহার করে মাল্টিথ্রেডিং সহজ, কার্যকরী এবং নিরাপদ হয়। এর ফলে একই সময়ে একাধিক কাজ এক্সিকিউট করা সম্ভব হয়।
- বৃহৎ ডেটার জন্য কার্যকরী:
- যখন ডেটা সেট বড় হয়, তখন parallelStream() বা ForkJoinPool ব্যবহার করে কোডের পারফরম্যান্স বৃদ্ধি করা সম্ভব হয়, কারণ এটি একাধিক থ্রেডে ডেটা প্রসেসিং করে।
- কমপ্লেক্স অ্যাসিনক্রোনাস কাজ:
- CompletableFuture ব্যবহার করে আপনি অ্যাসিনক্রোনাস কাজগুলি সহজে ম্যানেজ করতে পারেন এবং একাধিক অ্যাসিনক্রোনাস কাজকে চেইন করতে পারেন।
- স্ট্রিম এবং ফাংশনাল প্রোগ্রামিং:
- Streams API ব্যবহার করে functional style কোড লেখা সম্ভব, যা আরও পরিষ্কার এবং সহজ বোঝার মতো হয়।
map,filter,reduce,collectইত্যাদি মেথডের সাহায্যে ডেটার উপর কার্যকরী অপারেশন করা যায়।
- Streams API ব্যবহার করে functional style কোড লেখা সম্ভব, যা আরও পরিষ্কার এবং সহজ বোঝার মতো হয়।
সারসংক্ষেপ
Java 8-এ Concurrency Enhancements মাল্টিথ্রেডিং এবং কনকারেন্ট প্রোগ্রামিংকে আরো সহজ, কার্যকরী এবং কার্যকর করে তুলেছে। ForkJoinPool, CompletableFuture, Streams API এবং নতুন কনকারেন্সি ইউটিলিটি সহ, Java এখন মাল্টিথ্রেডিং ব্যবস্থাপনায় আরও সক্ষম এবং উচ্চ পারফরম্যান্স প্রদান করতে সক্ষম। Java 8 এর এই উন্নত বৈশিষ্ট্যগুলো ডেভেলপারদের জন্য একটি শক্তিশালী হাতিয়ার যা বড় ডেটাসেট এবং অ্যাসিনক্রোনাস অপারেশনগুলিকে আরও দক্ষভাবে পরিচালনা করতে সাহায্য করে।
Java 8-এ Concurrency বা Multithreading-এর জন্য বেশ কিছু গুরুত্বপূর্ণ উন্নয়ন (enhancements) আনা হয়েছে, যা প্রোগ্রামিংয়ের সিস্টেমের পারফরম্যান্স, স্কেলেবিলিটি এবং সহজতর করে তোলে। এই নতুন ফিচারগুলির মধ্যে প্রধান দুটি হল java.util.concurrent প্যাকেজের নতুন ক্লাস এবং java.util.stream API তে parallel streams। এগুলির সাহায্যে উন্নত ও কার্যকরী মাল্টিথ্রেডেড প্রোগ্রাম তৈরি করা সম্ভব হয়েছে।
Java 8-এ Concurrency Enhancements মূলত থ্রেড পরিচালনা, সিঙ্ক্রোনাইজেশন এবং কাজের কার্যকরী বিভাজন করতে সহায়তা করে। নিচে Java 8 এর কিছু গুরুত্বপূর্ণ Concurrency Enhancements এবং তার ব্যবহার আলোচনা করা হলো।
১. CompletableFuture API
Java 8-এ CompletableFuture একটি অত্যন্ত গুরুত্বপূর্ণ ফিচার হিসেবে এসেছে, যা অ্যাসিনক্রোনাস অপারেশন এবং ভবিষ্যৎ ফলাফলগুলি (future results) পরিচালনা করতে সহায়তা করে। এর মাধ্যমে non-blocking পদ্ধতিতে একাধিক কাজ একসাথে (asynchronously) করা যেতে পারে এবং এর সাথে বিভিন্ন callback এবং composing tasks করা সম্ভব।
CompletableFuture এর সুবিধা:
- Asynchronous Programming: এটি অ্যাসিনক্রোনাস অপারেশনগুলি সহজভাবে পরিচালনা করতে সাহায্য করে।
- Non-blocking I/O: I/O অপারেশনগুলিকে ব্লক না করে পার্শ্ব-প্রতিক্রিয়া করা যায়।
- Lambda Expressions: এটি lambda expressions এর মাধ্যমে কাজের ফ্লো সহজ করে তোলে।
উদাহরণ:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) throws Exception {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000); // Simulating a long task
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42; // Returning a result
});
// Use the result when it's ready
future.thenAccept(result -> System.out.println("Result: " + result)); // Output: Result: 42
// Waiting for the future to complete
future.get(); // Blocks the main thread until the result is ready
}
}এখানে, CompletableFuture.supplyAsync() ব্যবহার করা হয়েছে অ্যাসিনক্রোনাস কাজ (long task) চালানোর জন্য। thenAccept() মেথড ব্যবহার করে প্রাপ্ত ফলাফলটি প্রিন্ট করা হয়েছে।
২. ForkJoinPool এবং Parallel Streams
Java 8-এ ForkJoinPool ব্যবহারের মাধ্যমে সহজে parallel processing করা যায়। Java 8 এ parallel streams এর মাধ্যমে ডেটা প্রসেসিং আরও দ্রুত হয় কারণ এটি মাল্টিপল কোর ব্যবহার করে ডেটার উপরে কাজ করে।
Parallel Streams:
parallelStream() মেথড ব্যবহার করে স্ট্রিমের উপাদানগুলোকে parallelly প্রসেস করা হয়, যা অধিক কার্যকরী হয় যখন বড় ডেটাসেটের উপর কাজ করা হয়।
উদাহরণ:
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Parallel Stream
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("Sum of numbers: " + sum); // Output: 55
}
}এখানে, parallelStream() মেথড ব্যবহার করে সংখ্যাগুলির সমষ্টি parallel অপারেশনের মাধ্যমে করা হয়েছে। এটি CPU কোরগুলিকে ব্যবহার করে দ্রুত কাজ সম্পন্ন করে।
ForkJoinPool:
ForkJoinPool মাল্টিপল টাস্ককে সন্নিবেশিত (divide) করে বিভিন্ন থ্রেডে পাঠায়, যাতে বড় টাস্কগুলিকে দ্রুত পারফর্ম করা যায়।
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
public class ForkJoinPoolExample {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
// Example task: calculating factorial
RecursiveTask<Integer> task = new FactorialTask(5);
Integer result = pool.invoke(task);
System.out.println("Factorial Result: " + result); // Output: 120
}
}
class FactorialTask extends RecursiveTask<Integer> {
private final int number;
FactorialTask(int number) {
this.number = number;
}
@Override
protected Integer compute() {
if (number <= 1) {
return 1;
} else {
FactorialTask subTask = new FactorialTask(number - 1);
subTask.fork(); // Fork a new task
return number * subTask.join(); // Combine the result
}
}
}এখানে, ForkJoinPool ব্যবহার করে বড় কাজগুলোকে ছোট টাস্কে ভাগ করা হয়েছে এবং সেগুলোকে আলাদাভাবে প্রক্রিয়া করা হয়েছে। RecursiveTask ব্যবহৃত হয়েছে একটি রিকার্সিভ কাজের জন্য।
৩. Atomic ক্লাস এবং volatile Keyword
Java 8 এ Atomic ক্লাস (যেমন AtomicInteger, AtomicLong) এবং volatile কিওয়ার্ড মাল্টিথ্রেডিং এনভায়রনমেন্টে ডেটা সিঙ্ক্রোনাইজেশন সহজ করেছে। Atomic ক্লাসগুলি একে অপরের সাথে থ্রেড নিরাপদে কাজ করতে সাহায্য করে।
Atomic Variables Example:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(0);
// Increment atomically
atomicInteger.incrementAndGet(); // Atomically increments the value
System.out.println("Atomic Integer: " + atomicInteger.get()); // Output: 1
}
}এখানে, AtomicInteger ব্যবহার করা হয়েছে যাতে একাধিক থ্রেড একে অপরের সাথে হস্তক্ষেপ না করে মান পরিবর্তন করতে পারে। incrementAndGet() মেথড অ্যাটমিকভাবে মান বৃদ্ধি করে।
৪. Lambda Expressions এবং Concurrency
Java 8 এ Lambda Expressions ব্যবহার করে কোড আরও সহজ এবং পরিষ্কার করা হয়েছে। Lambda এক্সপ্রেশন ব্যবহার করে মাল্টিথ্রেডিং প্রোগ্রামে কাজ করা আরও স্বাভাবিক এবং সহজ হয়েছে।
Lambda এবং ExecutorService:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LambdaConcurrencyExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
// Lambda Expression for Runnable
executor.submit(() -> {
System.out.println("Task 1 is running on thread: " + Thread.currentThread().getName());
});
executor.submit(() -> {
System.out.println("Task 2 is running on thread: " + Thread.currentThread().getName());
});
executor.shutdown();
}
}এখানে, Lambda Expressions ব্যবহার করে Runnable এর জন্য কোড সহজভাবে লিখিত হয়েছে এবং ExecutorService এর মাধ্যমে বিভিন্ন থ্রেডে কাজগুলো সম্পন্ন করা হয়েছে।
সারসংক্ষেপ
Java 8 এর Concurrency Enhancements মাল্টিথ্রেডিং এবং অ্যাসিনক্রোনাস প্রোগ্রামিংকে আরও সহজ এবং কার্যকরী করেছে। কিছু মূল উন্নতি হলো:
CompletableFuture: অ্যাসিনক্রোনাস অপারেশন এবং ফিউচার রেজাল্ট ম্যানেজ করার জন্য।ForkJoinPoolএবং Parallel Streams: মাল্টি-থ্রেডেড প্রোগ্রামিং এবং ডেটা প্রসেসিংয়ের জন্য প্যারালাল কাজ করা।Atomicক্লাস: থ্রেড নিরাপদ ভ্যারিয়েবল ব্যবস্থাপনা।- Lambda Expressions: মাল্টিথ্রেডিং কোড লেখাকে সহজ এবং পরিষ্কার করা।
Java 8-এর এই নতুন Concurrency API মাল্টিথ্রেডিং প্রোগ্রামগুলোকে দ্রুত, কার্যকরী এবং সহজ করার জন্য ব্যবহৃত হচ্ছে।
Asynchronous Programming হল এমন একটি প্রোগ্রামিং পদ্ধতি যেখানে একটি কাজ ব্যাকগ্রাউন্ডে চলতে থাকে এবং সেই কাজের শেষ হওয়া পর্যন্ত অন্যান্য কাজগুলো চলতে থাকে। এর ফলে, আপনি একাধিক কাজ একসাথে করতে পারেন এবং একটি কাজের জন্য অপেক্ষা না করে অন্য কাজ করতে পারেন। Java 8 থেকে CompletableFuture ক্লাসটি যুক্ত করা হয়েছে, যা Asynchronous Programming সহজ এবং কার্যকরী করতে সহায়তা করে।
CompletableFuture এর ধারণা
CompletableFuture হল একটি Future ইন্টারফেসের একটি বাস্তবায়ন যা আপনাকে অ্যাসিঙ্ক্রোনাস অপারেশন (Asynchronous Operations) করতে সহায়তা করে। এটি একটি "future" অবজেক্ট যা ভবিষ্যতে কোনো নির্দিষ্ট মান বা ফলাফল প্রদান করবে।
CompletableFuture ক্লাসটি সাধারণ Future ইন্টারফেস থেকে উন্নত এবং এটি একটি non-blocking অপারেশন পরিচালনা করতে সাহায্য করে। এটি কার্যকরীভাবে callback ফাংশন ব্যবহার করে অ্যাসিঙ্ক্রোনাস কাজ পরিচালনা করতে পারে, যার ফলে প্রধান থ্রেড ব্লক (block) না হয় এবং অন্যান্য কাজগুলো চালিয়ে যেতে পারে।
CompletableFuture এর মেথডসমূহ
CompletableFuture বেশ কিছু শক্তিশালী মেথড সরবরাহ করে যা Asynchronous Programming-কে আরও শক্তিশালী এবং সুবিধাজনক করে তোলে। এখানে কিছু মূল মেথড আলোচনা করা হলো:
supplyAsync(): অ্যাসিঙ্ক্রোনাসভাবে একটি টাস্ক চালানোর জন্য ব্যবহৃত হয় এবং একটিCompletableFutureরিটার্ন করে।CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { return 10; });thenApply(): একটিFunctionব্যবহার করে প্রাপ্ত ফলাফলের উপর অপারেশন করতে ব্যবহৃত হয়। এটি একে অপরের সাথে কাজ করার জন্য একটি pipeline তৈরি করতে সহায়তা করে।CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> 10) .thenApply(value -> value * 2);thenAccept(): এটিthenApply()এর মতো, তবে এটি কোন মান রিটার্ন করে না। এটি প্রাপ্ত ফলাফলটি গ্রহণ করে এবং এটি ব্যবহার করে কোনো কাজ করতে সহায়তা করে।CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> 10) .thenAccept(value -> System.out.println(value * 2));thenCombine(): দুটি আলাদাCompletableFutureএর ফলাফল একত্রিত করতে ব্যবহৃত হয়।CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10); CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20); CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (a, b) -> a + b);allOf(): একাধিক অ্যাসিঙ্ক্রোনাস টাস্ক সম্পন্ন হওয়ার জন্য অপেক্ষা করতে ব্যবহৃত হয়।CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> 10); CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> 20); CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2); allOfFuture.join();exceptionally(): কোনো ব্যতিক্রম (exception) সংঘটিত হলে তা পরিচালনা করতে ব্যবহৃত হয়।CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { if (true) throw new RuntimeException("Something went wrong!"); return 10; }).exceptionally(ex -> { System.out.println("Exception: " + ex.getMessage()); return 0; });
Asynchronous Programming এর সুবিধা
- Non-blocking I/O: অ্যাসিঙ্ক্রোনাস প্রোগ্রামিংকে ব্যবহার করে আপনি I/O অপারেশনগুলো ব্লক না করে অন্যান্য কাজ চালিয়ে যেতে পারেন, যেমন নেটওয়ার্ক কল, ডাটাবেস অপারেশন, ইত্যাদি।
- Parallelism: একাধিক কাজ একসাথে করা সম্ভব।
CompletableFutureএর মাধ্যমে একাধিক প্রসেস একযোগে (parallel) চালানো যায়, যেমন একাধিক API কল করা বা সার্ভারের মধ্যে বিভিন্ন ডেটা প্রসেস করা। - Better Utilization of CPU Cores: অ্যাসিঙ্ক্রোনাস কোড মাল্টি-কোর প্রসেসরের সম্পূর্ণ ব্যবহার করতে সাহায্য করে। যখন একটি কাজ ব্যাকগ্রাউন্ডে চলছে, তখন অন্যান্য থ্রেড কাজ চালিয়ে যেতে পারে, ফলে CPU কোরের পূর্ণ ব্যবহার করা হয়।
- Improved Performance: অ্যাসিঙ্ক্রোনাস প্রোগ্রামিং, বিশেষ করে নেটওয়ার্ক এবং I/O ব্যস্ত কাজের জন্য, পারফরম্যান্স বৃদ্ধি করতে সহায়তা করে কারণ আপনি কাজগুলো একে অপরের সাথে সমান্তরালভাবে চালাতে পারেন।
CompletableFuture এর উদাহরণ
উদাহরণ ১: supplyAsync() এবং thenApply() ব্যবহার
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
// supplyAsync() দিয়ে অ্যাসিঙ্ক্রোনাস কাজ শুরু
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // Simulate some delay
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10;
});
// thenApply() দিয়ে আসা ফলাফলের ওপর কাজ করা
future.thenApply(result -> {
System.out.println("Result from async task: " + result);
return result * 2;
}).thenAccept(result -> {
System.out.println("Final result: " + result);
});
// Main thread won't block; it will continue executing other tasks.
System.out.println("Main thread continues working...");
}
}এখানে, supplyAsync() একটি ব্যাকগ্রাউন্ড থ্রেডে কাজ শুরু করে এবং তারপর thenApply() ব্যবহার করে প্রাপ্ত মানের উপর কিছু অপারেশন করা হয়।
উদাহরণ ২: allOf() ব্যবহার
import java.util.concurrent.CompletableFuture;
public class AllOfExample {
public static void main(String[] args) {
// Multiple asynchronous tasks
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> 20);
// Wait for both futures to complete
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(future1, future2);
allOfFuture.join(); // Wait for all futures to complete
System.out.println("Both tasks completed.");
}
}এখানে, allOf() ব্যবহার করে একাধিক অ্যাসিঙ্ক্রোনাস কাজের জন্য একসাথে অপেক্ষা করা হয়েছে।
সারসংক্ষেপ
CompletableFutureJava 8-এ Asynchronous Programming সহজ করতে সাহায্য করে এবং এটি non-blocking I/O এবং parallel task execution সমর্থন করে।CompletableFutureএর মেথডগুলো যেমনsupplyAsync(),thenApply(),thenAccept(),allOf()ইত্যাদি, অ্যাসিঙ্ক্রোনাস কার্যক্রমের সঙ্গতি এবং কার্যকরীতা বৃদ্ধির জন্য ব্যবহৃত হয়।- Asynchronous Programming বিশেষ করে যখন ব্যাকগ্রাউন্ড টাস্ক বা I/O ব্যস্ত কাজের প্রয়োজন হয়, তখন এটি পারফরম্যান্স বাড়াতে এবং CPU কোরের সম্পূর্ণ ব্যবহার করতে সাহায্য করে।
Fork/Join Framework হল Java 7 এ যুক্ত একটি শক্তিশালী কনক্যারেন্সি ফিচার যা মাল্টিথ্রেডিংয়ের মাধ্যমে বড় ধরনের কাজগুলো ছোট ছোট টাস্কে ভাগ করে দ্রুত এক্সিকিউট করতে সহায়ক। এটি কার্যকরীভাবে পারালাল প্রসেসিং, ডিভাইড অ্যান্ড কনকার (Divide and Conquer) প্রক্রিয়া এবং মাল্টি-কোর প্রসেসর ব্যবহার করতে সক্ষম। Java 7 এ ForkJoinPool ক্লাসের মাধ্যমে এই ফ্রেমওয়ার্কটি প্রবর্তিত হয়, যা মাল্টিপল থ্রেডগুলোকে একই সময়ে একাধিক কাজ একত্রে সমাধান করার জন্য সমন্বয় করে।
Java 8 এবং পরবর্তী সংস্করণে Fork/Join Framework এর কিছু উন্নত বৈশিষ্ট্য এবং পদ্ধতি যুক্ত করা হয়েছে, যা প্রোগ্রামের পারফরম্যান্স আরও উন্নত করতে সহায়ক।
১. Fork/Join Framework এর মূল ধারণা
Fork/Join Framework মূলত ForkJoinPool ক্লাসের মাধ্যমে কাজ করে, যা একটি থ্রেড পুল ব্যবস্থাপনা করে। থ্রেড পুলের মধ্যে কাজগুলো ভাগ করে (fork) এবং পরবর্তী সময়ে তাদের সমন্বয় করে (join) একত্রিত করা হয়।
এটির মূল ধারণা হল:
- Fork: একটি বড় কাজকে ছোট ছোট কাজ বা টাস্কে ভাগ করা।
- Join: সমস্ত ছোট টাস্কের ফলাফল একত্রিত করা।
ForkJoinPool দুইটি প্রধান কার্যকারিতা দিয়ে কাজ করে:
- RecursiveTask: এটি একটি কাজ যা একটি রিটার্ন ভ্যালু প্রদান করে।
- RecursiveAction: এটি একটি কাজ যা কোনো রিটার্ন ভ্যালু প্রদান করে না (void)।
২. Fork/Join Framework এর উন্নত সংস্করণ
Java 8-এ Fork/Join Framework এর কিছু উন্নতি এবং নতুন ফিচার যুক্ত করা হয়েছে যা পারফরম্যান্স এবং ব্যবহারকারীর অভিজ্ঞতা উন্নত করেছে:
১. Stream API Integration:
Java 8-এ Fork/Join ব্যবস্থাপনা এবং Stream API-এর একটি শক্তিশালী সংযোগ রয়েছে। এটি parallelStream() ব্যবহার করে মাল্টি-থ্রেডিংয়ের মাধ্যমে ডেটা প্রসেসিং করতে সহায়ক। ForkJoinPool এখন Stream.parallel() মেথডের মাধ্যমে স্ট্রিম অপারেশনগুলির পারফরম্যান্স উন্নত করে।
উদাহরণ:
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public class ForkJoinExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Parallel Stream using Fork/Join Framework
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
System.out.println("Sum of numbers: " + sum); // Output: Sum of numbers: 55
}
}এখানে parallelStream() স্ট্রিমের মধ্যে Fork/Join Framework ব্যবহার করে ডেটা এক্সিকিউট করেছে। এটি বিভিন্ন থ্রেডে কাজ ভাগ করে দ্রুত ফলাফল প্রদান করে।
২. Custom Work Stealing:
Java 8-এ Work Stealing কৌশলটি আরও উন্নত করা হয়েছে। এখন আপনি ForkJoinPool এর মধ্যে থ্রেডের কাজের ভাগ বদলাতে পারেন (stealing work from other threads) যাতে কোনও থ্রেড অব্যবহৃত না থাকে এবং সবথেকে দ্রুত কাজ করে।
৩. RecursiveTask/RecursiveAction Optimizations:
Java 8-এ RecursiveTask এবং RecursiveAction ক্লাসগুলির পারফরম্যান্স উন্নত করা হয়েছে। এটি বড় কাজগুলিকে ছোট ছোট কাজের মধ্যে ভাগ করার সময় আরও দক্ষতার সাথে কাজ করে, যাতে কাজের ভাগ আরও ভালোভাবে সম্পন্ন হতে পারে এবং পারফরম্যান্স উন্নত হয়।
উদাহরণ:
import java.util.concurrent.RecursiveTask;
public class ForkJoinSumExample extends RecursiveTask<Long> {
private final long[] arr;
private final int low;
private final int high;
public ForkJoinSumExample(long[] arr, int low, int high) {
this.arr = arr;
this.low = low;
this.high = high;
}
@Override
protected Long compute() {
if (high - low <= 1) {
return arr[low]; // Base case: return the element at index low
} else {
int mid = (low + high) / 2;
ForkJoinSumExample left = new ForkJoinSumExample(arr, low, mid);
ForkJoinSumExample right = new ForkJoinSumExample(arr, mid, high);
left.fork(); // Fork the left task
long rightResult = right.compute(); // Compute the right task
long leftResult = left.join(); // Join the left task result
return leftResult + rightResult;
}
}
public static void main(String[] args) {
long[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
ForkJoinPool pool = new ForkJoinPool();
ForkJoinSumExample task = new ForkJoinSumExample(arr, 0, arr.length);
long result = pool.invoke(task);
System.out.println("Sum: " + result); // Output: Sum: 55
}
}এখানে, আমরা একটি অ্যারে এর উপাদানগুলো যোগ করার জন্য RecursiveTask ব্যবহার করছি। যখন উপাদানের সংখ্যা একটি নির্দিষ্ট সীমার নিচে চলে যায়, তখন এটি ফর্ক করার পরিবর্তে সরাসরি ফলাফল প্রদান করে। তবে যখন বড় আকারে কাজ হচ্ছে, তখন এটি কাজগুলো ভাগ করে এবং সমান্তরালভাবে এক্সিকিউট করা হয়।
৩. Improved Scalability:
Java 8-এ ForkJoinPool এর স্কেলেবিলিটি উন্নত করা হয়েছে। নতুন commonPool() মেথড দ্বারা ForkJoinPool ব্যবহারের সময় পুরো সিস্টেমের জন্য একটি কমন পুল তৈরি করা হয়, যাতে একাধিক থ্রেডে কাজ ভাগ করে কার্যকরভাবে পারফরম্যান্স বৃদ্ধি করা যায়।
৪. CompletionService:
Java 8-এ CompletionService ক্লাসটি যুক্ত করা হয়েছে, যা আপনার ForkJoinPool কাজের জন্য সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস কাজের ফলাফল দ্রুত সংগ্রহ করতে সহায়ক। এর মাধ্যমে থ্রেড পুলের মাধ্যমে কাজের ফলাফল দ্রুত পাওয়া যায়।
উদাহরণ:
import java.util.concurrent.*;
public class ForkJoinExampleWithCompletionService {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
CompletionService<Integer> completionService = new ExecutorCompletionService<>(executor);
// Submitting tasks
for (int i = 0; i < 5; i++) {
final int taskId = i;
completionService.submit(() -> {
TimeUnit.SECONDS.sleep(2); // Simulate some work
return taskId * 2; // Return result
});
}
// Collecting results
for (int i = 0; i < 5; i++) {
Future<Integer> future = completionService.take();
System.out.println("Result: " + future.get());
}
executor.shutdown();
}
}এখানে, ExecutorCompletionService এবং ForkJoinPool এর মাধ্যমে একাধিক কাজ সম্পাদন করা হয়েছে এবং তাদের ফলাফল একে একে সংগ্রহ করা হয়েছে।
সারসংক্ষেপ
- Fork/Join Framework Java 7 এ মাল্টি-কোর প্রসেসর ব্যবহার করে বড় কাজগুলিকে ছোট ছোট টাস্কে ভাগ করার জন্য তৈরি করা হয়েছিল।
- Java 8-এ Fork/Join Framework এর উন্নত সংস্করণ এসেছে, যা Stream API, Custom Work Stealing, RecursiveTask/RecursiveAction অপটিমাইজেশন, এবং CompletionService এর মতো নতুন বৈশিষ্ট্যসমূহ অন্তর্ভুক্ত করেছে।
- এই ফিচারগুলো ব্যবহার করে বড় ডেটা সেট বা কম্প্লেক্স প্রসেসিংয়ের কাজগুলো দ্রুত, কার্যকরভাবে এবং সমান্তরালভাবে সম্পাদন করা যায়।
এগুলো parallelism এবং concurrency বৃদ্ধির মাধ্যমে সিস্টেমের পারফরম্যান্স উন্নত করতে সহায়ক।
Java 8 এ ConcurrentHashMap-এ কিছু গুরুত্বপূর্ণ নতুন ফিচার এবং অপারেশন যোগ করা হয়েছে যা মাল্টিথ্রেডিং এবং কনকারেন্ট প্রোগ্রামিং এর ক্ষেত্রে কার্যকারিতা উন্নত করেছে। ConcurrentHashMap একটি থ্রেড-সেফ Map অ্যাপ্লিকেশন, যা একাধিক থ্রেডের দ্বারা একযোগভাবে অ্যাক্সেস করা হলেও ভালো পারফরম্যান্স প্রদান করে। এটি java.util.concurrent প্যাকেজে অন্তর্ভুক্ত।
Java 8-এ ConcurrentHashMap এর নতুন ফিচারগুলো মূলত Lambda Expressions, forEach(), compute(), merge(), replaceAll() এবং computeIfAbsent() সহ আরও অনেক কার্যকরী মেথডের মাধ্যমে ব্যাপকভাবে উন্নত করা হয়েছে। এই ফিচারগুলো ব্যবহার করে আপনি আরও কার্যকরীভাবে কনকারেন্ট প্রোগ্রামিং করতে পারেন।
1. forEach() Method (Java 8)
Java 8-এ ConcurrentHashMap এ নতুন forEach() মেথড যোগ করা হয়েছে, যা একটি lambda expression ব্যবহার করে ম্যাপের সমস্ত এন্ট্রি উপাদানগুলোকে iterate (পর্যালোচনা) করতে ব্যবহৃত হয়। এটি BiConsumer ইনটারফেস গ্রহণ করে।
উদাহরণ:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapForEachExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
// Lambda expression ব্যবহার করে forEach মেথড
map.forEach((key, value) -> {
System.out.println("Key: " + key + ", Value: " + value);
});
}
}Output:
Key: 1, Value: One
Key: 2, Value: Two
Key: 3, Value: Threeএখানে, forEach() মেথডে ল্যাম্বডা এক্সপ্রেশন ব্যবহার করা হয়েছে যা ম্যাপের সমস্ত কিপেয়ার (key-value pair) প্রিন্ট করে।
2. compute() Method (Java 8)
compute() মেথডটি একটি কিপেয়ার (key-value pair) আপডেট করতে ব্যবহৃত হয়, যদি যে key-এর জন্য নতুন ভ্যালু সেট করতে চান তা ম্যাপে না থাকে, তাহলে এটি BiFunction ইনটারফেস ব্যবহার করে নতুন ভ্যালু তৈরি করে।
উদাহরণ:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapComputeExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
map.put(2, "Two");
// compute() মেথড ব্যবহার করে ভ্যালু পরিবর্তন করা
map.compute(1, (key, value) -> value + " Updated");
map.compute(3, (key, value) -> "Three");
System.out.println(map);
}
}Output:
{1=One Updated, 2=Two, 3=Three}এখানে, compute() মেথড 1 কিপেয়ার এর ভ্যালুকে আপডেট করেছে এবং 3 কিপেয়ারটি নতুন করে যোগ করেছে।
3. computeIfAbsent() Method (Java 8)
computeIfAbsent() মেথডটি নির্দিষ্ট কিপেয়ার (key) উপস্থিত না থাকলে নতুন ভ্যালু তৈরি করার জন্য ব্যবহার করা হয়। এটি শুধুমাত্র যদি key আগেই ম্যাপে না থাকে তবে নতুন ভ্যালু তৈরি করবে।
উদাহরণ:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapComputeIfAbsentExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
// computeIfAbsent() ব্যবহার করে key উপস্থিত না থাকলে ভ্যালু যোগ করা
map.computeIfAbsent(2, key -> "Two");
System.out.println(map);
}
}Output:
{1=One, 2=Two}এখানে, 2 কিপেয়ারটি computeIfAbsent() মেথডের মাধ্যমে যোগ করা হয়েছে কারণ এটি আগে থেকেই ম্যাপে ছিল না।
4. merge() Method (Java 8)
merge() মেথডটি দুইটি কিপেয়ার (key-value pair) একত্রিত করতে ব্যবহৃত হয়, যখন দুটি key একই হয়। এটি BiFunction গ্রহণ করে যা দুটি ভ্যালুকে একত্রিত করে।
উদাহরণ:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapMergeExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
map.put(2, "Two");
// merge() ব্যবহার করে দুইটি key-value একত্রিত করা
map.merge(2, "Updated", (oldValue, newValue) -> oldValue + " " + newValue);
map.merge(3, "Three", (oldValue, newValue) -> oldValue + " " + newValue);
System.out.println(map);
}
}Output:
{1=One, 2=Two Updated, 3=Three}এখানে, merge() মেথড 2 কিপেয়ারটির ভ্যালুকে আপডেট করেছে এবং 3 কিপেয়ারটি নতুনভাবে যোগ করেছে।
5. replaceAll() Method (Java 8)
replaceAll() মেথডটি সমস্ত ভ্যালুকে একটি নতুন ভ্যালু দ্বারা প্রতিস্থাপন করতে ব্যবহৃত হয়, যেখানে আপনি একটি BiFunction পাস করে নির্দিষ্ট মানের পরিবর্তন করতে পারেন।
উদাহরণ:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapReplaceAllExample {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
map.put(2, "Two");
// replaceAll() মেথড ব্যবহার করে সমস্ত ভ্যালু পরিবর্তন
map.replaceAll((key, value) -> value.toUpperCase());
System.out.println(map);
}
}Output:
{1=ONE, 2=TWO}এখানে, replaceAll() মেথড সব ভ্যালুকে uppercase-এ পরিবর্তন করেছে।
সারসংক্ষেপ
Java 8-এ ConcurrentHashMap-এ অনেক নতুন ফিচার এবং মেথড যোগ করা হয়েছে, যা thread-safeভাবে ম্যাপের উপাদানগুলির সাথে কার্যকরভাবে কাজ করতে সাহায্য করে। বিশেষ করে forEach(), compute(), computeIfAbsent(), merge(), এবং replaceAll() মেথডগুলি ব্যবহার করে আপনি আরও কার্যকরী এবং নমনীয় কোড লিখতে পারবেন।
forEach(): ম্যাপের উপাদানগুলোর উপর ল্যাম্বডা এক্সপ্রেশন প্রয়োগ করা।compute(): একটি কিপেয়ার আপডেট করা।computeIfAbsent(): নির্দিষ্ট কিপেয়ার উপস্থিত না থাকলে নতুন ভ্যালু তৈরি করা।merge(): দুটি কিপেয়ার একত্রিত করা।replaceAll(): সমস্ত ভ্যালু পরিবর্তন করা।
এগুলো ConcurrentHashMap-কে আরও শক্তিশালী এবং নমনীয় করে তোলে মাল্টিথ্রেডেড পরিবেশে ডেটা ম্যানিপুলেশন করতে।
Read more