"হাই, অ্যামিগো!"

"হাই, এলি!"

"আমি আপনাকে উদ্বায়ী পরিবর্তনকারী সম্পর্কে বলতে চাই। আপনি কি জানেন যে এটি কি?"

"থ্রেডের সাথে কিছু করার আছে। আমার ঠিক মনে নেই।"

"তাহলে শুনুন। এখানে আপনার জন্য কিছু প্রযুক্তিগত বিবরণ রয়েছে:"

"একটি কম্পিউটারে দুই ধরনের মেমরি থাকে: গ্লোবাল (সাধারণ) মেমরি এবং প্রসেসরে তৈরি মেমরি। অন্তর্নির্মিত প্রসেসর মেমরি রেজিস্টারে বিভক্ত, একটি প্রথম-স্তরের ক্যাশে (L1), দ্বিতীয়-স্তরের ক্যাশে (L2) এবং তৃতীয় স্তর (L3)।"

"এই ধরনের মেমরির গতি ভিন্ন। দ্রুততম এবং সবচেয়ে ছোট মেমরি হল রেজিস্টার, তারপর প্রসেসর ক্যাশে (L1, L2, L3), এবং অবশেষে গ্লোবাল মেমরি (সবচেয়ে ধীর)।"

"গ্লোবাল মেমরি এবং প্রসেসর ক্যাশে অত্যন্ত ভিন্ন গতিতে কাজ করে, তাই জাভা মেশিন প্রতিটি থ্রেডকে স্থানীয় থ্রেড মেমরিতে (প্রসেসর ক্যাশে) সর্বাধিক ব্যবহৃত ভেরিয়েবল সংরক্ষণ করতে দেয়।"

"এই প্রক্রিয়াটি কি কোনোভাবে নিয়ন্ত্রণ করা যায়?"

"আসলে না। সমস্ত কাজ জাভা মেশিন দ্বারা করা হয়। পারফরম্যান্স অপ্টিমাইজ করার ক্ষেত্রে এটি খুবই বুদ্ধিমান।"

"কিন্তু এখানে কেন আমি আপনাকে এটি বলছি। এখানে একটি ছোট সমস্যা আছে। যখন দুটি থ্রেড একই ভেরিয়েবলের সাথে কাজ করে, তখন প্রতিটি তার নিজস্ব স্থানীয় ক্যাশে একটি কপি সংরক্ষণ করতে পারে। এবং তারপর একটি থ্রেড পরিবর্তনশীল পরিবর্তন করতে পারে, কিন্তু দ্বিতীয়টি পরিবর্তনটি দেখতে নাও পারে, কারণ এটি এখনও ভেরিয়েবলের নিজস্ব অনুলিপি নিয়ে কাজ করছে।"

"আচ্ছা, তাহলে কি করা যায়?"

"জাভার নির্মাতারা এই পরিস্থিতির জন্য একটি বিশেষ কীওয়ার্ড প্রদান করেছেন: উদ্বায়ী। যদি একটি ভেরিয়েবল বিভিন্ন থ্রেড থেকে অ্যাক্সেস করা হয়, তাহলে আপনাকে এটিকে উদ্বায়ী পরিবর্তনকারী দিয়ে চিহ্নিত করতে হবে, তাই জাভা মেশিন এটিকে ক্যাশে রাখে না। সাধারণত এভাবেই দেখায়:"

public volatile int count = 0;

"ওহ, আমার মনে আছে। আপনি ইতিমধ্যে এটি উল্লেখ করেছেন। আমি এটি ইতিমধ্যেই জানি।"

"অবশ্যই করবেন। কিন্তু আমি যখন তোমাকে বলেছিলাম তখনই তোমার মনে পড়েছিল।"

"আরে, আচ্ছা, আমি একটু ভুলে গেছি।"

"পুনরাবৃত্তি শেখার জননী!"

"এখানে উদ্বায়ী সংশোধক সম্পর্কে কয়েকটি নতুন তথ্য রয়েছে। উদ্বায়ী পরিবর্তনকারী শুধুমাত্র গ্যারান্টি দেয় যে ভেরিয়েবলটি নিরাপদে পড়া এবং লেখা হবে। এটি নিশ্চিত করে না যে এটি নিরাপদে পরিবর্তন করা হবে।"

"কি পার্থক্য?"

"একটি ভেরিয়েবল কিভাবে পরিবর্তিত হয় তা দেখুন:"

কোড আসলে কি ঘটে: বর্ণনা
count++
register = count;

register = register+1;

count = register;
ধাপ 1.
পরিবর্তনশীল গণনার মান গ্লোবাল মেমরি থেকে একটি প্রসেসর রেজিস্টারে কপি করা হয়।

ধাপ 2.
প্রসেসরের ভিতরে, রেজিস্টার ভেরিয়েবল 1 দ্বারা বৃদ্ধি করা হয়।

ধাপ 3.
ভেরিয়েবলের মান প্রসেসর থেকে গ্লোবাল মেমরিতে কপি করা হয়।

"বাহ! তাই, সব ভেরিয়েবল শুধুমাত্র প্রসেসরে পরিবর্তন করা হয়?"

"হ্যাঁ।"

"এবং মানগুলি সামনে পিছনে অনুলিপি করা হয়: মেমরি থেকে প্রসেসর এবং পিছনে?"

"হ্যাঁ।"

"অস্থির সংশোধক গ্যারান্টি দেয় যে যখন পরিবর্তনশীল গণনা অ্যাক্সেস করা হবে তখন এটি মেমরি থেকে পড়া হবে (ধাপ 1)। এবং যদি একটি থ্রেড একটি নতুন মান নির্ধারণ করতে চায়, তবে এটি অবশ্যই বিশ্বব্যাপী মেমরিতে থাকবে (ধাপ 3)।"

"কিন্তু জাভা মেশিন গ্যারান্টি দেয় না যে ধাপ 1 এবং 3 এর মধ্যে কোন থ্রেড স্যুইচিং হবে না।"

"তাহলে, ভেরিয়েবলকে 1 দ্বারা বৃদ্ধি করা আসলে তিনটি অপারেশন?"

"হ্যাঁ."

"এবং যদি দুটি থ্রেড একই সাথে গণনা ++ চালাতে চায়, তাহলে তারা একে অপরের সাথে হস্তক্ষেপ করতে পারে?"

"হ্যাঁ, এটি পরীক্ষা করে দেখুন:"

থ্রেড 1 থ্রেড 2 ফলাফল
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

"সুতরাং, আপনি ভেরিয়েবল অ্যাক্সেস করতে পারেন, কিন্তু এটি পরিবর্তন করা এখনও ঝুঁকিপূর্ণ?"

"ঠিক আছে, আপনি এটি পরিবর্তন করতে পারেন, শুধু সাবধান ☺"

"কিভাবে?"

" সিঙ্ক্রোনাইজ  আমাদের সেরা বন্ধু।"

"আমি দেখি."