Java Memory Model (JMM) জাভা প্ল্যাটফর্মের একটি গুরুত্বপূর্ণ অংশ, যা মাল্টিথ্রেডেড প্রোগ্রামিংয়ের সময় থ্রেড এবং মেমোরির মধ্যে যোগাযোগ নিয়ন্ত্রণ করে। এটি নির্ধারণ করে যে, থ্রেডগুলোর মাধ্যমে ডেটা কীভাবে শেয়ার করা হবে এবং কীভাবে পরিবর্তিত ডেটা অন্য থ্রেড দ্বারা দৃশ্যমান হবে।
JMM একটি বিশেষ স্পেসিফিকেশন যা বর্ণনা করে:
volatile
নিশ্চিত করে যে একটি ভেরিয়েবলের সব রিড এবং রাইট অপারেশন মেমোরি থেকে সরাসরি করা হবে।উদাহরণ:
class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // মেইন মেমোরিতে আপডেট
}
public void reader() {
if (flag) {
System.out.println("Flag is true");
}
}
}
উদাহরণ:
class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
final
ভেরিয়েবল একবার ইনিশিয়ালাইজ করার পর পরিবর্তন করা যায় না। এটি থ্রেড-সেফ।উদাহরণ:
class FinalExample {
private final int number;
public FinalExample(int number) {
this.number = number; // একবার সেট করা হয়
}
public int getNumber() {
return number;
}
}
JMM happens-before
সম্পর্ক নিশ্চিত করে যে একটি নির্দিষ্ট অপারেশন অন্য অপারেশনের আগে ঘটবে। এটি থ্রেড সিঙ্ক্রোনাইজেশন এবং ভিজিবিলিটি নিশ্চিত করতে সাহায্য করে।
volatile
ভেরিয়েবল আপডেট করার পর সেটি অন্য থ্রেডে দৃশ্যমান হবে।class VisibilityExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // মেমোরিতে আপডেট
System.out.println("Flag updated to true");
}
public void reader() {
while (!flag) {
// অপেক্ষা করা
}
System.out.println("Flag is now true");
}
public static void main(String[] args) {
VisibilityExample example = new VisibilityExample();
Thread writerThread = new Thread(example::writer);
Thread readerThread = new Thread(example::reader);
readerThread.start();
writerThread.start();
}
}
ConcurrentHashMap
, CopyOnWriteArrayList
।ReentrantLock
, ReadWriteLock
।int x = 0, y = 0;
x = 1;
y = 2; // এই অর্ডার রি-অর্ডার হতে পারে
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class SharedResource {
private int value = 0;
public synchronized void increment() {
value++;
}
public synchronized int getValue() {
return value;
}
}
public class JMMExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
ExecutorService executor = Executors.newFixedThreadPool(2);
Runnable task1 = () -> {
for (int i = 0; i < 1000; i++) {
resource.increment();
}
};
Runnable task2 = () -> {
for (int i = 0; i < 1000; i++) {
resource.increment();
}
};
executor.execute(task1);
executor.execute(task2);
executor.shutdown();
while (!executor.isTerminated()) {
// অপেক্ষা করা
}
System.out.println("Final Value: " + resource.getValue());
}
}
Java Memory Model (JMM) হলো একটি স্পেসিফিকেশন যা সংজ্ঞায়িত করে কীভাবে Java প্রোগ্রামে থ্রেডগুলোর মধ্যে মেমরি শেয়ার এবং প্রসেস করা হবে। এটি Java Virtual Machine (JVM)-এর একটি অবিচ্ছেদ্য অংশ এবং এটি মাল্টিথ্রেডেড প্রোগ্রামিংয়ে ডেটা কনসিস্টেন্সি ও সঠিকতা নিশ্চিত করার জন্য গুরুত্বপূর্ণ।
synchronized
, volatile
, এবং Lock
) ব্যবহার করে।synchronized
: ব্লক বা মেথড লেভেলে মিউটেক্স লক প্রদান করে।volatile
: ভেরিয়েবল মেমরি ভিজিবিলিটি নিশ্চিত করে এবং রি-অর্ডারিং এড়ায়।java.util.concurrent.atomic
প্যাকেজের ক্লাস, যা থ্রেড-সেফ অপারেশন নিশ্চিত করে।JMM নিশ্চিত করে যে একটি থ্রেডের দ্বারা আপডেটকৃত ডেটা অন্য থ্রেডে দৃশ্যমান হবে।
class SharedData {
private volatile boolean flag = false;
public void writer() {
flag = true; // Update flag
}
public void reader() {
if (flag) {
System.out.println("Flag is true.");
}
}
}
ব্যাখ্যা:
volatile
ব্যবহার করা হয়েছে যাতে এক থ্রেডে আপডেট করা ডেটা অন্য থ্রেডে অবিলম্বে দৃশ্যমান হয়।JMM কম্পাইলার বা প্রসেসরের দ্বারা ইন্সট্রাকশন রি-অর্ডারিং প্রতিরোধ করে।
class ReorderingExample {
int a = 0, b = 0;
public void writer() {
a = 1; // Statement 1
b = 2; // Statement 2
}
public void reader() {
System.out.println("a = " + a + ", b = " + b);
}
}
ব্যাখ্যা:
synchronized
ব্যবহার করে থ্রেড-সেফ অপারেশন নিশ্চিত করা হয়।
class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
ব্যাখ্যা:
synchronized
ব্যবহার করে increment
এবং getCount
মেথডে একসঙ্গে একাধিক থ্রেডের অ্যাক্সেস সীমিত করা হয়েছে।synchronized
এবং volatile
এর মাধ্যমে এই সমস্যা সমাধান করে।Java Memory Model (JMM) মাল্টিথ্রেডিং প্রোগ্রামিংয়ে ডেটা কনসিস্টেন্সি, ভিজিবিলিটি, এবং সিঙ্ক্রোনাইজেশন নিশ্চিত করার জন্য একটি গুরুত্বপূর্ণ কাঠামো। synchronized
, volatile
, এবং Atomic ক্লাসগুলোর মতো কনকারেন্সি টুলস ব্যবহারের মাধ্যমে JMM থ্রেডের মধ্যে ডেটা শেয়ারিং এবং কমিউনিকেশন সহজ করে তোলে। দক্ষ মাল্টিথ্রেডেড অ্যাপ্লিকেশন ডেভেলপমেন্টের জন্য JMM এর ভূমিকা অপরিহার্য।
Happens-Before Relationship হলো জাভা মেমরি মডেলের (JMM) একটি গুরুত্বপূর্ণ কনসেপ্ট, যা থ্রেডের মধ্যে ক্রিয়াকলাপের অর্ডার এবং ডেটার ভিজিবিলিটি নির্ধারণ করে। এটি কনকারেন্সি প্রোগ্রামে নির্ভরযোগ্য এবং সঠিক কার্যক্রম নিশ্চিত করতে ব্যবহৃত হয়।
একটি থ্রেডের মধ্যে অপারেশনগুলি কোডে যেভাবে লেখা হয়েছে সেভাবেই কার্যকর হবে।
উদাহরণ:
int x = 10; // Happens before
int y = x + 5; // This depends on the value of x
একটি লক unlock
হওয়ার পরে, এর আগের সমস্ত অপারেশন অন্য কোনো থ্রেডের জন্য দৃশ্যমান হবে যদি সেই থ্রেড পরে একই লক lock
করে।
উদাহরণ:
synchronized (lock) {
sharedVariable = 10; // Happens before
} // Unlock happens here
synchronized (lock) {
System.out.println(sharedVariable); // Reads 10
}
একটি volatile
ভেরিয়েবলে লেখা অপারেশন তার পরে অন্য থ্রেডের জন্য দৃশ্যমান হয় যা সেই ভেরিয়েবল পড়ে।
উদাহরণ:
private volatile boolean running = false;
public void stopRunning() {
running = true; // Happens before
}
public void run() {
while (!running) {
// Loop until running is true
}
}
একটি থ্রেড শুরু হওয়ার আগে তার মধ্যে সেট করা সমস্ত কাজ নতুন থ্রেডের জন্য দৃশ্যমান হবে।
উদাহরণ:
Thread thread = new Thread(() -> {
System.out.println(sharedVariable); // Reads value set before thread start
});
sharedVariable = 42; // Happens before
thread.start();
যখন একটি থ্রেড join()
এর মাধ্যমে শেষ হয়, তখন সেই থ্রেডের সমস্ত কাজ join()
কলের পরে দৃশ্যমান হবে।
উদাহরণ:
Thread thread = new Thread(() -> {
sharedVariable = 42; // Happens before
});
thread.start();
thread.join(); // Ensures sharedVariable = 42 is visible here
System.out.println(sharedVariable);
যদি A happens-before B
এবং B happens-before C
, তাহলে A happens-before C
।
উদাহরণ:
sharedVariable = 10; // A
synchronized (lock) {
// B
}
System.out.println(sharedVariable); // C
A
happens-before B
, and B
happens-before C
. Therefore, A
happens-before C
.
class SharedResource {
private int counter = 0;
public synchronized void increment() {
counter++; // Happens-before any thread reading counter
}
public synchronized int getCounter() {
return counter; // Reads updated value
}
}
volatile
class VisibilityExample {
private volatile boolean running = true;
public void stop() {
running = false; // Happens-before loop exit
}
public void run() {
while (running) {
// Continuously running until `running` is false
}
}
}
synchronized
, volatile
বা Lock
ব্যবহার করুন।join()
না ব্যবহার করাthread.join()
ব্যবহার করুন।Happens-Before Relationship জাভার কনকারেন্সি প্রোগ্রামিংয়ের মূল ভিত্তি। এটি থ্রেডের মধ্যে ডেটার ভিজিবিলিটি এবং অপারেশনের অর্ডার নিশ্চিত করে, যা ডেডলক, রেস কন্ডিশন, এবং সিঙ্ক্রোনাইজেশন সমস্যা সমাধানে সাহায্য করে। সঠিকভাবে এই নিয়মগুলো মেনে চললে জাভা কনকারেন্সি আরো নিরাপদ এবং কার্যকর হয়।
volatile
কীওয়ার্ড জাভার কনকারেন্সি মডেলে একটি বিশেষ গুরুত্বপূর্ণ ভূমিকা পালন করে। এটি একটি ভ্যারিয়েবলকে থ্রেড-সেফ করার জন্য ব্যবহৃত হয়, যা নিশ্চিত করে যে একটি ভ্যারিয়েবলের আপডেট সব থ্রেডে দৃশ্যমান থাকবে।
volatile
এর প্রধান বৈশিষ্ট্যvolatile
ভ্যারিয়েবলের মান যখন একটি থ্রেড পরিবর্তন করে, তখন তা অন্য থ্রেডের কাছে অবিলম্বে দৃশ্যমান হয়।volatile
নিশ্চিত করে যে কম্পাইলার বা প্রসেসর ইনস্ট্রাকশনগুলোর ক্রম পরিবর্তন করবে না।volatile
এর প্রয়োজনীয়তাvolatile
ব্যবহার তখন গুরুত্বপূর্ণ যখন:
volatile
এর ব্যবহারclass VolatileExample {
private static volatile boolean running = true;
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (running) {
System.out.println("Thread is running...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread stopped.");
});
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
running = false; // থ্রেড বন্ধ করার সিগন্যাল
System.out.println("Main thread set running to false.");
}
}
ব্যাখ্যা:
running
একটি volatile
ভ্যারিয়েবল। এটি নিশ্চিত করে যে যখন একটি থ্রেড মান পরিবর্তন করে, অন্য থ্রেড তৎক্ষণাৎ পরিবর্তনটি দেখতে পায়।class VolatileReorderingExample {
private static volatile boolean flag = false;
private static int value = 0;
public static void main(String[] args) {
Thread writer = new Thread(() -> {
value = 42; // স্টেপ ১
flag = true; // স্টেপ ২
});
Thread reader = new Thread(() -> {
if (flag) {
System.out.println("Value: " + value); // সবসময় 42 হওয়া উচিত
}
});
writer.start();
reader.start();
}
}
ব্যাখ্যা:
volatile flag
নিশ্চিত করে যে value = 42
এর পরে flag = true
এক্সিকিউট হবে এবং ইনস্ট্রাকশন রিঅর্ডারিং এড়ানো হবে।volatile
এর সীমাবদ্ধতাAtomicity নিশ্চিত করে না:
volatile
কেবল মেমরি ভিজিবিলিটি নিশ্চিত করে। এটি অপারেশনগুলোকে পরমাণুকরণ (atomic) করে না।count++
একটি volatile
ভ্যারিয়েবলে থ্রেড-সেফ নয়।সমাধান: AtomicInteger
বা synchronized
ব্যবহার করতে হবে।
class NonAtomicVolatileExample {
private static volatile int counter = 0;
public static void main(String[] args) {
Thread incrementer1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter++; // Not thread-safe
}
});
Thread incrementer2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter++; // Not thread-safe
}
});
incrementer1.start();
incrementer2.start();
try {
incrementer1.join();
incrementer2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Counter Value: " + counter); // ফলাফল অপ্রত্যাশিত হতে পারে
}
}
সমাধান: AtomicInteger
ব্যবহার করুন।
import java.util.concurrent.atomic.AtomicInteger;
class AtomicExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
Thread incrementer1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet();
}
});
Thread incrementer2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet();
}
});
incrementer1.start();
incrementer2.start();
try {
incrementer1.join();
incrementer2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Counter Value: " + counter.get()); // ফলাফল সঠিক
}
}
volatile
ব্যবহার করবেন?volatile
একটি লাইটওয়েট মেকানিজম যা মেমরি ভিজিবিলিটি নিশ্চিত করে।synchronized
বা Atomic
ক্লাস ব্যবহার করা উচিত।মাল্টিথ্রেডেড প্রোগ্রামিংয়ে Visibility এবং Ordering Problems সাধারণ সমস্যা। এগুলোর সমাধান নিশ্চিত করার জন্য জাভা বিভিন্ন প্রযুক্তি ও টুল সরবরাহ করে।
Visibility Problem হয় যখন একটি থ্রেডে আপডেট হওয়া ডেটা অন্য থ্রেডের কাছে দৃশ্যমান হয় না। এর মূল কারণ হলো থ্রেডের নিজস্ব CPU Cache যেখানে ডেটা স্থানীয়ভাবে সংরক্ষিত হয়।
volatile
কীওয়ার্ড ব্যবহার করা:উদাহরণ:
class VisibilityExample {
private volatile boolean running = true;
public void stop() {
running = false; // মেইন মেমরিতে আপডেট
}
public void run() {
while (running) {
// কাজ চলমান
}
System.out.println("Thread stopped.");
}
public static void main(String[] args) throws InterruptedException {
VisibilityExample example = new VisibilityExample();
Thread thread = new Thread(example::run);
thread.start();
Thread.sleep(1000); // থ্রেড ১ সেকেন্ড চালান
example.stop(); // থ্রেড বন্ধের জন্য সিগন্যাল দিন
thread.join();
}
}
Ordering Problem হয় যখন কোডের এক্সিকিউশন সিকোয়েন্স (অর্ডার) প্রোগ্রামের লজিকের সঙ্গে মেলে না। এই সমস্যা Instruction Reordering এর কারণে ঘটে, যা জাভার কম্পাইলার বা প্রসেসর করে থাকে।
volatile
কীওয়ার্ড ব্যবহার করা:উদাহরণ:
class OrderingExample {
private volatile int value = 0;
private volatile boolean flag = false;
public void writer() {
value = 42; // Step 1
flag = true; // Step 2 (এই অর্ডার মেনটেইন করা হবে)
}
public void reader() {
if (flag) { // Step 3
System.out.println("Value: " + value); // Step 4 (value অবশ্যই 42 হবে)
}
}
public static void main(String[] args) {
OrderingExample example = new OrderingExample();
Thread writerThread = new Thread(example::writer);
Thread readerThread = new Thread(example::reader);
writerThread.start();
readerThread.start();
}
}
synchronized
ব্লক ব্যবহার করা:উদাহরণ:
class SynchronizedOrdering {
private int value = 0;
public synchronized void writer() {
value = 42; // মেইন মেমরিতে আপডেট
}
public synchronized int reader() {
return value; // মেইন মেমরি থেকে পড়া
}
public static void main(String[] args) {
SynchronizedOrdering example = new SynchronizedOrdering();
Thread writerThread = new Thread(example::writer);
Thread readerThread = new Thread(() -> {
System.out.println("Read Value: " + example.reader());
});
writerThread.start();
readerThread.start();
}
}
java.util.concurrent
প্যাকেজ ব্যবহার করা:AtomicInteger
, ReentrantLock
ইত্যাদি ব্যবহার করলে Visibility এবং Ordering সমস্যা সমাধান হয়।উদাহরণ:
import java.util.concurrent.atomic.AtomicInteger;
class AtomicExample {
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet(); // Atomically increment
}
public int getValue() {
return counter.get(); // Atomically get value
}
public static void main(String[] args) {
AtomicExample example = new AtomicExample();
Thread incrementThread = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
incrementThread.start();
try {
incrementThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Counter Value: " + example.getValue());
}
}
কৌশল/প্রযুক্তি | Visibility নিশ্চিত করে | Ordering নিশ্চিত করে |
---|---|---|
volatile | ✅ | ✅ |
synchronized | ✅ | ✅ |
Atomic Classes | ✅ | ✅ |
volatile
, synchronized
, অথবা Atomic
ক্লাস ব্যবহার করুন।volatile
, synchronized
, অথবা java.util.concurrent
টুল ব্যবহার করুন।Read more