মেমরি হার্ডওয়্যার আর্কিটেকচার
আধুনিক মেমরি হার্ডওয়্যার আর্কিটেকচার জাভার অভ্যন্তরীণ মেমরি মডেল থেকে আলাদা। অতএব, জাভা মডেল এটির সাথে কীভাবে কাজ করে তা জানার জন্য আপনাকে হার্ডওয়্যার আর্কিটেকচার বুঝতে হবে। এই বিভাগে সাধারণ মেমরি হার্ডওয়্যার আর্কিটেকচার বর্ণনা করে এবং পরবর্তী বিভাগে জাভা এটির সাথে কীভাবে কাজ করে তা বর্ণনা করে।
এখানে একটি আধুনিক কম্পিউটারের হার্ডওয়্যার আর্কিটেকচারের একটি সরলীকৃত চিত্র রয়েছে:
আধুনিক বিশ্বে, একটি কম্পিউটারে 2 বা তার বেশি প্রসেসর রয়েছে এবং এটি ইতিমধ্যেই আদর্শ। এর মধ্যে কিছু প্রসেসর একাধিক কোরও থাকতে পারে। এই ধরনের কম্পিউটারে, একই সময়ে একাধিক থ্রেড চালানো সম্ভব। প্রতিটি প্রসেসর কোর যে কোনো সময়ে একটি থ্রেড কার্যকর করতে সক্ষম। এর মানে হল যে কোনো জাভা অ্যাপ্লিকেশন একটি অগ্রাধিকার মাল্টি-থ্রেডেড, এবং আপনার প্রোগ্রামের মধ্যে, প্রতি প্রসেসর কোর একটি থ্রেড এক সময়ে চলতে পারে।
প্রসেসর কোরে রেজিস্টারের একটি সেট থাকে যা এর মেমরিতে থাকে (কোরের ভিতরে)। এটি কম্পিউটারের প্রধান মেমরিতে (র্যাম) থাকা ডেটার তুলনায় রেজিস্টার ডেটাতে অনেক দ্রুত কাজ করে। কারণ প্রসেসর এই রেজিস্টারগুলোকে অনেক দ্রুত অ্যাক্সেস করতে পারে।
প্রতিটি CPU এর নিজস্ব ক্যাশে স্তরও থাকতে পারে। বেশিরভাগ আধুনিক প্রসেসরে এটি রয়েছে। প্রসেসর প্রধান মেমরির তুলনায় অনেক দ্রুত তার ক্যাশে অ্যাক্সেস করতে পারে, কিন্তু অভ্যন্তরীণ রেজিস্টারের মতো দ্রুত নয়। ক্যাশে অ্যাক্সেস গতির মান প্রায় প্রধান মেমরি এবং অভ্যন্তরীণ রেজিস্টারের অ্যাক্সেস গতির মধ্যে।
তাছাড়া, প্রসেসরের একটি মাল্টি-লেভেল ক্যাশে থাকার জায়গা আছে। কিন্তু জাভা মেমরি মডেল হার্ডওয়্যার মেমরির সাথে কীভাবে ইন্টারঅ্যাক্ট করে তা বোঝার জন্য এটি জানা এত গুরুত্বপূর্ণ নয়। এটা জানা গুরুত্বপূর্ণ যে প্রসেসরের ক্যাশে কিছু স্তর থাকতে পারে।
যেকোনো কম্পিউটারে একইভাবে RAM (প্রধান মেমরি এরিয়া) থাকে। সমস্ত কোর প্রধান মেমরি অ্যাক্সেস করতে পারে। প্রধান মেমরি এরিয়া সাধারণত প্রসেসর কোরের ক্যাশে মেমরির চেয়ে অনেক বড় হয়।
এই মুহুর্তে যখন প্রসেসরের প্রধান মেমরিতে অ্যাক্সেসের প্রয়োজন হয়, এটি তার ক্যাশে মেমরিতে এটির কিছু অংশ পড়ে। এটি ক্যাশে থেকে কিছু ডেটা তার অভ্যন্তরীণ রেজিস্টারে পড়তে পারে এবং তারপরে তাদের উপর ক্রিয়াকলাপ সম্পাদন করতে পারে। যখন CPU-কে মূল মেমরিতে ফলাফল লেখার প্রয়োজন হয়, তখন এটি তার অভ্যন্তরীণ রেজিস্টার থেকে ক্যাশে ডাটা ফ্লাশ করে, এবং কিছু সময়ে, প্রধান মেমরিতে।
ক্যাশে সংরক্ষিত ডেটা সাধারণত প্রধান মেমরিতে ফিরে যায় যখন প্রসেসরকে ক্যাশে অন্য কিছু সংরক্ষণ করার প্রয়োজন হয়। ক্যাশে এর মেমরি সাফ করার এবং একই সাথে ডেটা লেখার ক্ষমতা রয়েছে। প্রতিবার আপডেটের সময় প্রসেসরকে সম্পূর্ণ ক্যাশে পড়তে বা লিখতে হবে না। সাধারণত ক্যাশে মেমরির ছোট ব্লকে আপডেট করা হয়, তাদের বলা হয় "ক্যাশে লাইন"। এক বা একাধিক "ক্যাশ লাইন" ক্যাশে মেমরিতে পড়া হতে পারে, এবং এক বা একাধিক ক্যাশে লাইন মেইন মেমরিতে ফিরে যেতে পারে।
জাভা মেমরি মডেল এবং মেমরি হার্ডওয়্যার আর্কিটেকচারের সমন্বয়
ইতিমধ্যে উল্লিখিত হিসাবে, জাভা মেমরি মডেল এবং মেমরি হার্ডওয়্যার আর্কিটেকচার ভিন্ন। হার্ডওয়্যার আর্কিটেকচার থ্রেড স্ট্যাক এবং গাদা মধ্যে পার্থক্য করে না। হার্ডওয়্যারে, থ্রেড স্ট্যাক এবং HEAP (হিপ) প্রধান মেমরিতে থাকে।
স্ট্যাক এবং থ্রেডের স্তূপগুলির অংশগুলি কখনও কখনও ক্যাশে এবং CPU এর অভ্যন্তরীণ রেজিস্টারে উপস্থিত থাকতে পারে। এটি চিত্রে দেখানো হয়েছে:
যখন বস্তু এবং ভেরিয়েবল কম্পিউটারের মেমরির বিভিন্ন এলাকায় সংরক্ষণ করা যায়, তখন কিছু সমস্যা দেখা দিতে পারে। এখানে দুটি প্রধান হল:
- থ্রেড ভাগ করা ভেরিয়েবলে যে পরিবর্তনগুলি করেছে তার দৃশ্যমানতা।
- ভাগ করা ভেরিয়েবল পড়া, পরীক্ষা করা এবং লেখার সময় রেসের অবস্থা।
এই উভয় সমস্যা নীচে ব্যাখ্যা করা হবে.
ভাগ করা বস্তুর দৃশ্যমানতা
যদি দুই বা ততোধিক থ্রেড অস্থির ঘোষণা বা সিঙ্ক্রোনাইজেশনের যথাযথ ব্যবহার ছাড়াই একটি বস্তু ভাগ করে, তাহলে একটি থ্রেড দ্বারা তৈরি করা ভাগ করা বস্তুর পরিবর্তনগুলি অন্য থ্রেডগুলিতে দৃশ্যমান নাও হতে পারে।
কল্পনা করুন যে একটি ভাগ করা বস্তু প্রাথমিকভাবে প্রধান মেমরিতে সংরক্ষণ করা হয়। একটি সিপিইউতে চলমান একটি থ্রেড একই সিপিইউ-এর ক্যাশে ভাগ করা বস্তুটি পড়ে। সেখানে সে বস্তুর পরিবর্তন করে। যতক্ষণ না CPU-এর ক্যাশে মূল মেমরিতে ফ্লাশ করা হয়, শেয়ার করা বস্তুর পরিবর্তিত সংস্করণ অন্যান্য CPU-তে চলমান থ্রেডগুলিতে দৃশ্যমান হয় না। সুতরাং, প্রতিটি থ্রেড ভাগ করা বস্তুর নিজস্ব অনুলিপি পেতে পারে, প্রতিটি অনুলিপি একটি পৃথক CPU ক্যাশে থাকবে।
নিম্নলিখিত চিত্রটি এই পরিস্থিতির একটি রূপরেখা চিত্রিত করে। বাম CPU-তে চলমান একটি থ্রেড শেয়ার্ড অবজেক্টটিকে তার ক্যাশে কপি করে এবং গণনার মান 2-এ পরিবর্তন করে। ডান CPU-তে চলমান অন্যান্য থ্রেডের কাছে এই পরিবর্তনটি অদৃশ্য কারণ গণনার আপডেটটি এখনও মূল মেমরিতে ফিরে আসেনি।
এই সমস্যা সমাধানের জন্য, আপনি ভেরিয়েবল ঘোষণা করার সময় উদ্বায়ী কীওয়ার্ড ব্যবহার করতে পারেন। এটি নিশ্চিত করতে পারে যে একটি প্রদত্ত ভেরিয়েবল সরাসরি প্রধান মেমরি থেকে পড়া হয় এবং আপডেট করার সময় সর্বদা প্রধান মেমরিতে লেখা হয়।
জাতি শর্ত
যদি দুই বা ততোধিক থ্রেড একই অবজেক্ট শেয়ার করে এবং একাধিক থ্রেড সেই শেয়ার করা অবজেক্টে ভেরিয়েবল আপডেট করে, তাহলে রেসের অবস্থা হতে পারে।
কল্পনা করুন যে থ্রেড A তার প্রসেসরের ক্যাশে শেয়ার্ড অবজেক্টের কাউন্ট ভেরিয়েবল পড়ে। কল্পনা করুন যে থ্রেড বি একই জিনিস করে, কিন্তু অন্য প্রসেসরের ক্যাশে। এখন থ্রেড A গণনার মান 1 যোগ করে, এবং থ্রেড B একই কাজ করে। এখন ভেরিয়েবলটি দুইবার বাড়ানো হয়েছে - প্রতিটি প্রসেসরের ক্যাশে আলাদাভাবে +1 দ্বারা।
যদি এই বৃদ্ধিগুলি ক্রমানুসারে সঞ্চালিত হয়, গণনা ভেরিয়েবল দ্বিগুণ করা হবে এবং মূল মেমরিতে (মূল মান + 2) লেখা হবে।
যাইহোক, সঠিক সিঙ্ক্রোনাইজেশন ছাড়াই একই সময়ে দুটি ইনক্রিমেন্ট সঞ্চালিত হয়েছিল। যাই হোক না কেন থ্রেড (A বা B) মূল মেমরিতে গণনার আপডেট করা সংস্করণ লিখছে, দুটি বৃদ্ধি সত্ত্বেও নতুন মানটি মূল মানের থেকে 1 বেশি হবে।
এই চিত্রটি উপরে বর্ণিত রেসের অবস্থার সমস্যার ঘটনাকে চিত্রিত করে:
এই সমস্যাটি সমাধান করতে, আপনি জাভা সিঙ্ক্রোনাইজড ব্লক ব্যবহার করতে পারেন। একটি সিঙ্ক্রোনাইজড ব্লক নিশ্চিত করে যে কোনো নির্দিষ্ট সময়ে শুধুমাত্র একটি থ্রেড কোডের একটি প্রদত্ত সমালোচনামূলক বিভাগে প্রবেশ করতে পারে।
সিঙ্ক্রোনাইজড ব্লকগুলিও গ্যারান্টি দেয় যে সিঙ্ক্রোনাইজড ব্লকের ভিতরে অ্যাক্সেস করা সমস্ত ভেরিয়েবল প্রধান মেমরি থেকে পড়া হবে এবং যখন থ্রেডটি সিঙ্ক্রোনাইজড ব্লক থেকে প্রস্থান করবে, সমস্ত আপডেট করা ভেরিয়েবলগুলিকে মূল মেমরিতে ফিরিয়ে দেওয়া হবে, ভেরিয়েবলটিকে উদ্বায়ী বা না ঘোষণা করা হোক না কেন।
GO TO FULL VERSION