CodeGym /Java Blog /এলোমেলো /একসাথে ভাল: জাভা এবং থ্রেড ক্লাস। পার্ট II — সিঙ্ক্রোনাইজ...
John Squirrels
লেভেল 41
San Francisco

একসাথে ভাল: জাভা এবং থ্রেড ক্লাস। পার্ট II — সিঙ্ক্রোনাইজেশন

এলোমেলো দলে প্রকাশিত

ভূমিকা

সুতরাং, আমরা জানি যে জাভা থ্রেড আছে. আপনি এটি সম্পর্কে আরও ভাল একসাথে শিরোনামের পর্যালোচনাতে পড়তে পারেন : জাভা এবং থ্রেড ক্লাস। পার্ট I — থ্রেডস অফ এক্সিকিউশন । সমান্তরালভাবে কাজ করার জন্য থ্রেডগুলি প্রয়োজনীয়। এটি অত্যন্ত সম্ভাবনা তৈরি করে যে থ্রেডগুলি কোনওভাবে একে অপরের সাথে যোগাযোগ করবে। আসুন দেখি কিভাবে এটি ঘটে এবং আমাদের কাছে কোন মৌলিক সরঞ্জাম রয়েছে। একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 1

ফলন

Thread.yield() বিস্ময়কর এবং খুব কমই ব্যবহৃত হয়। এটি ইন্টারনেটে বিভিন্ন উপায়ে বর্ণনা করা হয়েছে। কিছু লোক লিখছে যে থ্রেডের কিছু সারি রয়েছে, যেখানে থ্রেড অগ্রাধিকারের ভিত্তিতে একটি থ্রেড নেমে আসবে। অন্যরা লেখেন যে একটি থ্রেড তার স্ট্যাটাস "চালানো" থেকে "চালাতে যোগ্য" তে পরিবর্তন করবে (যদিও এই স্ট্যাটাসের মধ্যে কোন পার্থক্য নেই, অর্থাৎ জাভা তাদের মধ্যে পার্থক্য করে না)। বাস্তবতা হল যে এটি সব অনেক কম সুপরিচিত এবং তবুও এক অর্থে সহজ। একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 2একটি বাগ আছে ( JDK-6416721: (স্পেক থ্রেড) ফিক্স Thread.yield() javadoc ) yield()পদ্ধতির ডকুমেন্টেশনের জন্য লগ করা হয়েছে। যদি আপনি এটি পড়ুন, এটা স্পষ্ট যেyield()মেথড আসলে জাভা থ্রেড শিডিউলারকে কিছু সুপারিশ প্রদান করে যে এই থ্রেডটিকে কম কার্যকর করার সময় দেওয়া যেতে পারে। কিন্তু আসলে কী ঘটে, যেমন শিডিউলকারী সুপারিশের উপর কাজ করে কিনা এবং সাধারণভাবে এটি কী করে, তা নির্ভর করে JVM এর বাস্তবায়ন এবং অপারেটিং সিস্টেমের উপর। এবং এটি কিছু অন্যান্য কারণের উপরও নির্ভর করতে পারে। জাভা ভাষার বিকাশের সাথে সাথে মাল্টিথ্রেডিং নিয়ে পুনর্বিবেচনা করা হয়েছে এই কারণেই সম্ভবত সমস্ত বিভ্রান্তি। এখানে ওভারভিউতে আরও পড়ুন: Java Thread.yield() এর সংক্ষিপ্ত ভূমিকা

ঘুম

একটি থ্রেড তার মৃত্যুদন্ডের সময় ঘুমাতে যেতে পারে। এটি অন্যান্য থ্রেডের সাথে মিথস্ক্রিয়া করার সবচেয়ে সহজ প্রকার। যে অপারেটিং সিস্টেমটি জাভা ভার্চুয়াল মেশিন চালায় যা আমাদের জাভা কোড চালায় তার নিজস্ব থ্রেড শিডিউলার রয়েছে । এটি কোন থ্রেড শুরু এবং কখন সিদ্ধান্ত নেয়। একজন প্রোগ্রামার জাভা কোড থেকে সরাসরি এই শিডিউলারের সাথে যোগাযোগ করতে পারে না, শুধুমাত্র JVM এর মাধ্যমে। তিনি বা তিনি সময়সূচীকে কিছুক্ষণের জন্য থ্রেডটি বিরতি দিতে বলতে পারেন, অর্থাৎ এটিকে ঘুমাতে দিতে। আপনি এই নিবন্ধগুলিতে আরও পড়তে পারেন: Thread.sleep() এবং মাল্টিথ্রেডিং কীভাবে কাজ করে । আপনি উইন্ডোজ অপারেটিং সিস্টেমে থ্রেডগুলি কীভাবে কাজ করে তাও দেখতে পারেন: উইন্ডোজ থ্রেডের অভ্যন্তরীণ । এবং এখন আসুন এটি আমাদের নিজের চোখে দেখি। নামের একটি ফাইলে নিম্নলিখিত কোড সংরক্ষণ করুন HelloWorldApp.java:

class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
আপনি দেখতে পাচ্ছেন, আমাদের কিছু টাস্ক রয়েছে যা 60 সেকেন্ডের জন্য অপেক্ষা করে, যার পরে প্রোগ্রামটি শেষ হয়। আমরা " " কমান্ড ব্যবহার করে কম্পাইল করি এবং তারপর " " javac HelloWorldApp.javaব্যবহার করে প্রোগ্রাম চালাই । java HelloWorldAppএকটি পৃথক উইন্ডোতে প্রোগ্রামটি শুরু করা ভাল। উদাহরণস্বরূপ, উইন্ডোজে, এটি এরকম: start java HelloWorldApp. আমরা পিআইডি (প্রসেস আইডি) পেতে jps কমান্ড ব্যবহার করি, এবং আমরা " jvisualvm --openpid pid: দিয়ে থ্রেডের তালিকা খুলি একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 3, যেমন আপনি দেখতে পাচ্ছেন, আমাদের থ্রেডের এখন "স্লিপিং" অবস্থা রয়েছে৷ আসলে, সাহায্য করার আরও মার্জিত উপায় রয়েছে৷ আমাদের থ্রেড মিষ্টি স্বপ্ন আছে:

try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Woke up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
আপনি কি লক্ষ্য করেছেন যে আমরা InterruptedExceptionসর্বত্র পরিচালনা করছি? আসুন কেন বুঝতে পারি।

Thread.interrupt()

জিনিসটি হল যখন একটি থ্রেড অপেক্ষা করছে/ঘুমছে, কেউ বাধা দিতে চাইতে পারে। এই ক্ষেত্রে, আমরা একটি হ্যান্ডেল InterruptedException. Thread.stop()পদ্ধতিটিকে অবচ্যুত, অর্থাৎ পুরানো এবং অবাঞ্ছিত ঘোষণা করার পরে এই প্রক্রিয়া তৈরি করা হয়েছিল । কারণটি ছিল যখন stop()পদ্ধতিটি বলা হয়েছিল, থ্রেডটি কেবল "হত্যা" ছিল, যা খুব অনির্দেশ্য ছিল। আমরা জানতে পারিনি কখন থ্রেডটি বন্ধ করা হবে এবং আমরা ডেটা সামঞ্জস্যের গ্যারান্টি দিতে পারিনি। কল্পনা করুন যে থ্রেডটি মারা যাওয়ার সময় আপনি একটি ফাইলে ডেটা লিখছেন। থ্রেডটি মেরে ফেলার পরিবর্তে, জাভার নির্মাতারা সিদ্ধান্ত নিয়েছিলেন যে এটিকে বাধা দেওয়া উচিত তা বলা আরও যুক্তিযুক্ত হবে। কীভাবে এই তথ্যের প্রতিক্রিয়া জানাবেন তা থ্রেডের নিজেই সিদ্ধান্ত নেওয়ার বিষয়। আরও বিস্তারিত জানার জন্য, পড়ুন কেন Thread.stop অবচয়?ওরাকলের ওয়েবসাইটে। আসুন একটি উদাহরণ দেখি:

public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
এই উদাহরণে, আমরা 60 সেকেন্ড অপেক্ষা করব না। পরিবর্তে, আমরা অবিলম্বে "বিঘ্নিত" প্রদর্শন করব। interrupt()আমরা থ্রেড উপর পদ্ধতি কল কারণ এটি হয় . এই পদ্ধতিটি "ইন্টারপ্ট স্ট্যাটাস" নামে একটি অভ্যন্তরীণ পতাকা সেট করে। অর্থাৎ, প্রতিটি থ্রেডের একটি অভ্যন্তরীণ পতাকা রয়েছে যা সরাসরি অ্যাক্সেসযোগ্য নয়। কিন্তু এই পতাকার সাথে ইন্টারঅ্যাক্ট করার জন্য আমাদের দেশীয় পদ্ধতি আছে। কিন্তু এটাই একমাত্র উপায় নয়। একটি থ্রেড চলমান হতে পারে, কিছুর জন্য অপেক্ষা না করে, কেবল কর্ম সম্পাদন করে। তবে এটি অনুমান করতে পারে যে অন্যরা একটি নির্দিষ্ট সময়ে এর কাজ শেষ করতে চাইবে। উদাহরণ স্বরূপ:

public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			// Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
উপরের উদাহরণে, whileথ্রেডটি বাহ্যিকভাবে বাধা না হওয়া পর্যন্ত লুপটি কার্যকর করা হবে। পতাকার ক্ষেত্রে isInterrupted, এটা জানা গুরুত্বপূর্ণ যে যদি আমরা একটি ধরি InterruptedException, তাহলে isInterrupted পতাকা রিসেট হবে এবং তারপর isInterrupted()মিথ্যা ফিরে আসবে। থ্রেড ক্লাসের একটি স্ট্যাটিক Thread.interrupted() পদ্ধতিও রয়েছে যা শুধুমাত্র বর্তমান থ্রেডের ক্ষেত্রে প্রযোজ্য, কিন্তু এই পদ্ধতিটি পতাকাটিকে মিথ্যাতে রিসেট করে! থ্রেড ইন্টারপশন শিরোনামের এই অধ্যায়ে আরও পড়ুন ।

যোগ দিন (অন্য থ্রেড শেষ হওয়ার জন্য অপেক্ষা করুন)

অপেক্ষার সহজ ধরনটি অন্য থ্রেড শেষ হওয়ার জন্য অপেক্ষা করছে।

public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
এই উদাহরণে, নতুন থ্রেড 5 সেকেন্ড ঘুমাবে। একই সময়ে, মূল থ্রেডটি ঘুমন্ত থ্রেডটি জেগে ওঠা এবং তার কাজ শেষ না হওয়া পর্যন্ত অপেক্ষা করবে। আপনি যদি JVisualVM-এ থ্রেডের অবস্থা দেখেন, তাহলে এটি এইরকম দেখাবে: একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 4মনিটরিং সরঞ্জামগুলির জন্য ধন্যবাদ, আপনি থ্রেডের সাথে কী চলছে তা দেখতে পারেন। পদ্ধতিটি joinবেশ সহজ, কারণ এটি জাভা কোড সহ একটি পদ্ধতি যা কার্যকর করে wait()যতক্ষণ পর্যন্ত এটিকে বলা হয় থ্রেডটি জীবিত থাকে। থ্রেডটি মারা যাওয়ার সাথে সাথে (যখন এটি এর কাজ শেষ হয়), অপেক্ষা বাধাগ্রস্ত হয়। এবং যে পদ্ধতির সব জাদু join(). সুতরাং, এর সবচেয়ে আকর্ষণীয় জিনিস এগিয়ে চলুন.

মনিটর

মাল্টিথ্রেডিং একটি মনিটর ধারণা অন্তর্ভুক্ত. মনিটর শব্দটি 16 শতকের ল্যাটিন উপায়ে ইংরেজিতে এসেছে এবং এর অর্থ "একটি যন্ত্র বা ডিভাইস যা একটি প্রক্রিয়া পর্যবেক্ষণ, পরীক্ষা বা ক্রমাগত রেকর্ড রাখার জন্য ব্যবহৃত হয়"। এই নিবন্ধের প্রেক্ষাপটে, আমরা মূল বিষয়গুলি কভার করার চেষ্টা করব। যে কেউ বিশদ জানতে চান, অনুগ্রহ করে লিঙ্ক করা উপকরণগুলিতে ডুব দিন। আমরা জাভা ল্যাঙ্গুয়েজ স্পেসিফিকেশন (JLS): 17.1 দিয়ে আমাদের যাত্রা শুরু করি। সিঙ্ক্রোনাইজেশন । এটি নিম্নলিখিত বলে: একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 5এটি দেখা যাচ্ছে যে জাভা থ্রেডগুলির মধ্যে সিঙ্ক্রোনাইজেশনের জন্য একটি "মনিটর" প্রক্রিয়া ব্যবহার করে। একটি মনিটর প্রতিটি বস্তুর সাথে যুক্ত থাকে এবং থ্রেডগুলি এটির সাথে এটি অর্জন করতে পারে lock()বা এটি দিয়ে ছেড়ে দিতে পারে unlock()। এর পরে, আমরা ওরাকল ওয়েবসাইটে টিউটোরিয়ালটি খুঁজে পাব: অভ্যন্তরীণ লক এবং সিঙ্ক্রোনাইজেশন. এই টিউটোরিয়ালটি বলে যে জাভার সিঙ্ক্রোনাইজেশন একটি অভ্যন্তরীণ সত্তার চারপাশে তৈরি করা হয়েছে যাকে একটি অভ্যন্তরীণ লক বা মনিটর লক বলা হয় । এই লকটিকে প্রায়ই " মনিটর " বলা হয়। আমরা আবার দেখতে পাচ্ছি যে জাভাতে প্রতিটি বস্তুর সাথে একটি অন্তর্নিহিত লক রয়েছে। আপনি Java - Intrinsic Locks এবং Synchronization পড়তে পারেন । এরপরে এটা বোঝা গুরুত্বপূর্ণ হবে কিভাবে জাভাতে একটি বস্তু মনিটরের সাথে যুক্ত হতে পারে। জাভাতে, প্রতিটি বস্তুর একটি শিরোনাম রয়েছে যা কোড থেকে প্রোগ্রামারের কাছে উপলব্ধ নয় এমন অভ্যন্তরীণ মেটাডেটা সঞ্চয় করে, কিন্তু যা বস্তুর সাথে সঠিকভাবে কাজ করার জন্য ভার্চুয়াল মেশিনের প্রয়োজন। অবজেক্ট হেডারে একটি "মার্ক ওয়ার্ড" রয়েছে, যা দেখতে এইরকম: একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

এখানে একটি JavaWorld নিবন্ধ রয়েছে যা খুবই উপযোগী: কিভাবে জাভা ভার্চুয়াল মেশিন থ্রেড সিঙ্ক্রোনাইজেশন সম্পাদন করে । এই নিবন্ধটি JDK বাগ-ট্র্যাকিং সিস্টেমে নিম্নলিখিত সমস্যাটির "সারাংশ" বিভাগের বিবরণের সাথে একত্রিত করা উচিত: JDK-8183909 । আপনি এখানে একই জিনিস পড়তে পারেন: JEP-8183909 । সুতরাং, জাভাতে, একটি মনিটর একটি বস্তুর সাথে যুক্ত থাকে এবং যখন থ্রেডটি লকটি অর্জন (বা পেতে) করার চেষ্টা করে তখন একটি থ্রেড ব্লক করতে ব্যবহৃত হয়। এখানে সবচেয়ে সহজ উদাহরণ:

public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
এখানে, বর্তমান থ্রেড (যেটির উপর কোডের এই লাইনগুলি কার্যকর করা হয়) synchronizedকীওয়ার্ড ব্যবহার করে মনিটর ব্যবহার করার চেষ্টা করেobject"\লক পেতে/অধিগ্রহণ করতে পরিবর্তনশীল। যদি অন্য কেউ মনিটরের জন্য প্রতিদ্বন্দ্বিতা না করে (অর্থাৎ একই বস্তু ব্যবহার করে অন্য কেউ সিঙ্ক্রোনাইজড কোড চালাচ্ছে না), তাহলে জাভা "বায়সড লকিং" নামক একটি অপ্টিমাইজেশন করার চেষ্টা করতে পারে। একটি প্রাসঙ্গিক ট্যাগ এবং কোন থ্রেডটি মনিটরের লকটির মালিক সে সম্পর্কে একটি রেকর্ড অবজেক্ট হেডারের মার্ক শব্দে যোগ করা হয়। এটি একটি মনিটর লক করার জন্য প্রয়োজনীয় ওভারহেড হ্রাস করে। যদি মনিটরটি আগে অন্য থ্রেডের মালিকানাধীন ছিল, তাহলে এই ধরনের লকিং যথেষ্ট নয়। JVM পরবর্তী ধরনের লকিং-এ সুইচ করে: "বেসিক লকিং"। এটি তুলনা এবং অদলবদল (CAS) অপারেশন ব্যবহার করে। আরও কী, অবজেক্ট হেডারের মার্ক শব্দটি নিজেই আর মার্ক শব্দটি সংরক্ষণ করে না, বরং এটি কোথায় সংরক্ষণ করা হয়েছে তার একটি রেফারেন্স, এবং ট্যাগটি পরিবর্তিত হয় যাতে JVM বুঝতে পারে যে আমরা মৌলিক লকিং ব্যবহার করছি। যদি একাধিক থ্রেড একটি মনিটরের জন্য প্রতিদ্বন্দ্বিতা করে (প্রতিদ্বন্দ্বিতা করে) (একজন লকটি অর্জন করেছে এবং একটি সেকেন্ড লকটি প্রকাশের জন্য অপেক্ষা করছে), তাহলে মার্ক শব্দের ট্যাগটি পরিবর্তিত হয় এবং মার্ক শব্দটি এখন মনিটরের একটি রেফারেন্স সংরক্ষণ করে। একটি বস্তু হিসাবে — JVM এর কিছু অভ্যন্তরীণ সত্তা। JDK Enchancement Proposal (JEP) তে যেমন বলা হয়েছে, এই সত্তাকে সংরক্ষণ করার জন্য এই পরিস্থিতির জন্য মেমরির নেটিভ হিপ এলাকায় জায়গা প্রয়োজন। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। এবং একটি সেকেন্ড লকটি প্রকাশের জন্য অপেক্ষা করছে), তারপর মার্ক শব্দের ট্যাগটি পরিবর্তিত হয় এবং মার্ক শব্দটি এখন মনিটরের একটি বস্তু হিসাবে একটি রেফারেন্স সংরক্ষণ করে — JVM এর কিছু অভ্যন্তরীণ সত্তা। JDK Enchancement Proposal (JEP) তে যেমন বলা হয়েছে, এই সত্তাকে সংরক্ষণ করার জন্য এই পরিস্থিতির জন্য মেমরির নেটিভ হিপ এলাকায় জায়গা প্রয়োজন। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। এবং একটি সেকেন্ড লকটি প্রকাশের জন্য অপেক্ষা করছে), তারপর মার্ক শব্দের ট্যাগটি পরিবর্তিত হয় এবং মার্ক শব্দটি এখন মনিটরের একটি বস্তু হিসাবে একটি রেফারেন্স সংরক্ষণ করে — JVM এর কিছু অভ্যন্তরীণ সত্তা। JDK Enchancement Proposal (JEP) তে যেমন বলা হয়েছে, এই সত্তাকে সংরক্ষণ করার জন্য এই পরিস্থিতির জন্য মেমরির নেটিভ হিপ এলাকায় জায়গা প্রয়োজন। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। এবং মার্ক শব্দটি এখন একটি বস্তু হিসাবে মনিটরের একটি রেফারেন্স সংরক্ষণ করে — JVM এর কিছু অভ্যন্তরীণ সত্তা। JDK Enchancement Proposal (JEP) তে যেমন বলা হয়েছে, এই সত্তাকে সংরক্ষণ করার জন্য এই পরিস্থিতির জন্য মেমরির নেটিভ হিপ এলাকায় জায়গা প্রয়োজন। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। এবং মার্ক শব্দটি এখন একটি বস্তু হিসাবে মনিটরের একটি রেফারেন্স সংরক্ষণ করে — JVM এর কিছু অভ্যন্তরীণ সত্তা। JDK Enchancement Proposal (JEP) তে যেমন বলা হয়েছে, এই সত্তাকে সংরক্ষণ করার জন্য এই পরিস্থিতির জন্য মেমরির নেটিভ হিপ এলাকায় জায়গা প্রয়োজন। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। এই অভ্যন্তরীণ সত্তার মেমরি অবস্থানের রেফারেন্স অবজেক্ট হেডারের মার্ক শব্দে সংরক্ষণ করা হবে। সুতরাং, একাধিক থ্রেডের মধ্যে ভাগ করা সংস্থানগুলিতে অ্যাক্সেস সিঙ্ক্রোনাইজ করার জন্য একটি মনিটর সত্যিই একটি প্রক্রিয়া। JVM এই মেকানিজমের বিভিন্ন বাস্তবায়নের মধ্যে সুইচ করে। সুতরাং, সরলতার জন্য, মনিটর সম্পর্কে কথা বলার সময়, আমরা আসলে তালা সম্পর্কে কথা বলছি। একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 7

সিঙ্ক্রোনাইজ করা (একটি লকের জন্য অপেক্ষা করা হচ্ছে)

আমরা আগে দেখেছি, একটি "সিঙ্ক্রোনাইজড ব্লক" (বা "সমালোচনামূলক বিভাগ") ধারণাটি মনিটরের ধারণার সাথে ঘনিষ্ঠভাবে সম্পর্কিত। একটি উদাহরণ দেখুন:

public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized(lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized(lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print(" " + i);
		}
		System.out.println(" ...");
	}
}
এখানে, প্রধান থ্রেডটি প্রথমে টাস্ক অবজেক্টটিকে নতুন থ্রেডে পাস করে এবং তারপরে অবিলম্বে লকটি অর্জন করে এবং এটির সাথে একটি দীর্ঘ অপারেশন করে (8 সেকেন্ড)। এই সমস্ত সময়, টাস্কটি এগিয়ে যেতে অক্ষম, কারণ এটি synchronizedব্লকে প্রবেশ করতে পারে না, কারণ লকটি ইতিমধ্যে অর্জিত হয়েছে। যদি থ্রেডটি লক পেতে না পারে তবে এটি মনিটরের জন্য অপেক্ষা করবে। যত তাড়াতাড়ি এটি লক পায়, এটি কার্যকর করা অব্যাহত থাকবে। যখন একটি থ্রেড একটি মনিটর থেকে প্রস্থান করে, এটি লকটি প্রকাশ করে। JVisualVM-এ, এটি এইরকম দেখায়: একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 8আপনি JVisualVM-এ দেখতে পাচ্ছেন, স্ট্যাটাস হল "মনিটর", যার অর্থ হল থ্রেডটি ব্লক করা হয়েছে এবং মনিটরটি নিতে পারে না। আপনি একটি থ্রেডের স্থিতি নির্ধারণ করতে কোড ব্যবহার করতে পারেন, তবে এইভাবে নির্ধারিত স্থিতির নামগুলি JVisualVM-এ ব্যবহৃত নামের সাথে মেলে না, যদিও তারা একই রকম। এই ক্ষেত্রে, দth1.getState()for loop এ বিবৃতি BLOCKED রিটার্ন করবে , কারণ যতক্ষণ লুপ চলছে ততক্ষণ lockঅবজেক্টের মনিটর mainথ্রেড দ্বারা দখল করা হয় এবং th1থ্রেডটি অবরুদ্ধ থাকে এবং লক প্রকাশ না হওয়া পর্যন্ত এগিয়ে যেতে পারে না। সিঙ্ক্রোনাইজড ব্লক ছাড়াও, একটি সম্পূর্ণ পদ্ধতি সিঙ্ক্রোনাইজ করা যেতে পারে। উদাহরণস্বরূপ, এখানে HashTableক্লাস থেকে একটি পদ্ধতি আছে:

public synchronized int size() {
	return count;
}
এই পদ্ধতিটি যে কোনো সময়ে শুধুমাত্র একটি থ্রেড দ্বারা কার্যকর করা হবে। আমরা কি সত্যিই তালা প্রয়োজন? হ্যাঁ, আমরা এটা প্রয়োজন. উদাহরণ পদ্ধতির ক্ষেত্রে, "এই" বস্তু (বর্তমান বস্তু) একটি লক হিসাবে কাজ করে। এই বিষয়ে এখানে একটি আকর্ষণীয় আলোচনা রয়েছে: একটি সিঙ্ক্রোনাইজড ব্লকের পরিবর্তে একটি সিঙ্ক্রোনাইজড পদ্ধতি ব্যবহার করার একটি সুবিধা আছে কি? . যদি পদ্ধতিটি স্ট্যাটিক হয়, তাহলে লকটি "এই" অবজেক্ট হবে না (কারণ একটি স্ট্যাটিক পদ্ধতির জন্য "এই" অবজেক্ট নেই), বরং একটি ক্লাস অবজেক্ট (উদাহরণস্বরূপ, ) Integer.class

অপেক্ষা করুন (একটি মনিটরের জন্য অপেক্ষা)। notify() এবং notifyAll() পদ্ধতি

থ্রেড ক্লাসের আরেকটি অপেক্ষার পদ্ধতি রয়েছে যা একটি মনিটরের সাথে যুক্ত। অসদৃশ sleep()এবং join(), এই পদ্ধতিটিকে কেবল বলা যাবে না। এর নাম হল wait()। পদ্ধতিটি waitমনিটরের সাথে যুক্ত বস্তুতে বলা হয় যার জন্য আমরা অপেক্ষা করতে চাই। আসুন একটি উদাহরণ দেখি:

public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // The task object will wait until it is notified via lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // After we are notified, we will wait until we can acquire the lock
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // We sleep. Then we acquire the lock, notify, and release the lock
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVM-এ, এটি এইরকম দেখায়: একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 10এটি কীভাবে কাজ করে তা বোঝার জন্য, মনে রাখবেন যে wait()এবং notify()পদ্ধতিগুলি এর সাথে যুক্ত java.lang.Object। এটা অদ্ভুত বলে মনে হতে পারে যে থ্রেড-সম্পর্কিত পদ্ধতি ক্লাসে আছে Object। কিন্তু এর কারণ এখন উন্মোচিত হচ্ছে। আপনি মনে রাখবেন যে জাভাতে প্রতিটি বস্তুর একটি শিরোনাম আছে। হেডারে মনিটরের তথ্য, অর্থাৎ লকের অবস্থা সহ বিভিন্ন হাউসকিপিং তথ্য রয়েছে। মনে রাখবেন, প্রতিটি অবজেক্ট বা ক্লাসের উদাহরণ, JVM-এ একটি অভ্যন্তরীণ সত্তার সাথে যুক্ত, যাকে বলা হয় অন্তর্নিহিত লক বা মনিটর। উপরের উদাহরণে, টাস্ক অবজেক্টের কোডটি নির্দেশ করে যে আমরা অবজেক্টের সাথে যুক্ত মনিটরের জন্য সিঙ্ক্রোনাইজড ব্লক প্রবেশ করি lock। যদি আমরা এই মনিটরের জন্য লকটি অর্জনে সফল হই, তাহলেwait()বলা হয়. কার্য সম্পাদনকারী থ্রেড অবজেক্টের মনিটরকে ছেড়ে দেবে , কিন্তু অবজেক্টের মনিটর lockথেকে বিজ্ঞপ্তির জন্য অপেক্ষারত থ্রেডের সারিতে প্রবেশ করবে । lockথ্রেডের এই সারিটিকে WAIT SET বলা হয়, যা এর উদ্দেশ্যকে আরও সঠিকভাবে প্রতিফলিত করে। যে, এটি একটি সারির চেয়ে একটি সেট বেশি। থ্রেড mainটাস্ক অবজেক্টের সাথে একটি নতুন থ্রেড তৈরি করে, এটি শুরু করে এবং 3 সেকেন্ড অপেক্ষা করে। এর ফলে নতুন থ্রেডটি mainথ্রেডের আগে লকটি অর্জন করতে এবং মনিটরের সারিতে প্রবেশ করতে সক্ষম হবে। এর পরে, mainথ্রেড নিজেই lockবস্তুর সিঙ্ক্রোনাইজড ব্লকে প্রবেশ করে এবং মনিটর ব্যবহার করে থ্রেড বিজ্ঞপ্তি সঞ্চালন করে। বিজ্ঞপ্তি পাঠানোর পরে, mainথ্রেডটি প্রকাশ করেlockঅবজেক্টের মনিটর, এবং নতুন থ্রেড, যা আগে lockঅবজেক্টের মনিটর প্রকাশের জন্য অপেক্ষা করছিল, এক্সিকিউশন চালিয়ে যাচ্ছে। notify()শুধুমাত্র একটি থ্রেড ( ) বা সারিতে থাকা সমস্ত থ্রেডে ( notifyAll()) একই সাথে একটি বিজ্ঞপ্তি পাঠানো সম্ভব । এখানে আরও পড়ুন: জাভাতে notify() এবং notifyAll() এর মধ্যে পার্থক্য । এটি লক্ষ্য করা গুরুত্বপূর্ণ যে বিজ্ঞপ্তির আদেশ JVM কীভাবে প্রয়োগ করা হয় তার উপর নির্ভর করে। এখানে আরও পড়ুন: কিভাবে notify এবং notifyAll দিয়ে অনাহার সমাধান করবেন? . কোনো বস্তু নির্দিষ্ট না করেই সিঙ্ক্রোনাইজেশন করা যেতে পারে। আপনি এটি করতে পারেন যখন কোডের একক ব্লকের পরিবর্তে একটি সম্পূর্ণ পদ্ধতি সিঙ্ক্রোনাইজ করা হয়। উদাহরণস্বরূপ, স্ট্যাটিক পদ্ধতির জন্য, লকটি একটি ক্লাস অবজেক্ট হবে (এর মাধ্যমে প্রাপ্ত .class):

public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
লক ব্যবহার করার ক্ষেত্রে, উভয় পদ্ধতি একই। যদি একটি পদ্ধতি স্ট্যাটিক না হয়, তাহলে বর্তমান ব্যবহার করে সিঙ্ক্রোনাইজেশন করা হবে instance, অর্থাৎ ব্যবহার করে this। যাইহোক, আমরা আগে বলেছি আপনি getState()একটি থ্রেডের স্ট্যাটাস পেতে পদ্ধতিটি ব্যবহার করতে পারেন। উদাহরণস্বরূপ, একটি মনিটরের জন্য অপেক্ষারত সারিতে থাকা একটি থ্রেডের জন্য, স্থিতিটি WAITING বা TIMED_WAITING হবে, যদি পদ্ধতিটি wait()একটি সময়সীমা নির্দিষ্ট করে থাকে। একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 11

https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java

থ্রেড জীবন চক্র

তার জীবনের সময়কালে, একটি থ্রেডের অবস্থা পরিবর্তিত হয়। প্রকৃতপক্ষে, এই পরিবর্তনগুলি থ্রেডের জীবনচক্রকে অন্তর্ভুক্ত করে। যত তাড়াতাড়ি একটি থ্রেড তৈরি করা হয়, তার অবস্থা NEW. এই অবস্থায়, নতুন থ্রেড এখনও চলছে না এবং জাভা থ্রেড শিডিউলার এখনও এটি সম্পর্কে কিছু জানে না। থ্রেড সময়সূচী থ্রেড সম্পর্কে জানতে, আপনাকে অবশ্যই thread.start()পদ্ধতিটি কল করতে হবে। তারপর থ্রেডটি RUNNABLE অবস্থায় স্থানান্তরিত হবে। ইন্টারনেটে প্রচুর ভুল ডায়াগ্রাম রয়েছে যা "চালাতে যোগ্য" এবং "চলমান" অবস্থার মধ্যে পার্থক্য করে। কিন্তু এটি একটি ভুল, কারণ জাভা "কাজ করার জন্য প্রস্তুত" (চালানো যায়) এবং "কাজ করা" (চলমান) এর মধ্যে পার্থক্য করে না। যখন একটি থ্রেড জীবিত থাকে কিন্তু সক্রিয় থাকে না (চালানযোগ্য নয়), এটি দুটি অবস্থার একটিতে থাকে:
  • অবরুদ্ধ — একটি জটিল বিভাগে প্রবেশের জন্য অপেক্ষা করছে, যেমন একটি synchronizedব্লক।
  • অপেক্ষা - কিছু শর্ত পূরণ করার জন্য অন্য থ্রেডের জন্য অপেক্ষা করা হচ্ছে।
যদি শর্ত সন্তুষ্ট হয়, তাহলে থ্রেড নির্ধারণকারী থ্রেড শুরু করে। যদি থ্রেডটি একটি নির্দিষ্ট সময় পর্যন্ত অপেক্ষা করে, তাহলে এর স্থিতি হল TIMED_WAITING৷ যদি থ্রেডটি আর চলমান না থাকে (এটি সমাপ্ত হয় বা একটি ব্যতিক্রম নিক্ষেপ করা হয়েছিল), তাহলে এটি TERMINATED স্থিতিতে প্রবেশ করে। একটি থ্রেডের অবস্থা খুঁজে বের করতে, getState()পদ্ধতিটি ব্যবহার করুন। থ্রেডেরও একটি isAlive()পদ্ধতি রয়েছে, যা থ্রেডটি বন্ধ না হলে সত্য ফিরে আসে।

লকসাপোর্ট এবং থ্রেড পার্কিং

জাভা 1.6 থেকে শুরু করে, লকসাপোর্ট নামে একটি আকর্ষণীয় প্রক্রিয়া উপস্থিত হয়েছিল। একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 12এই শ্রেণীটি এটি ব্যবহার করে এমন প্রতিটি থ্রেডের সাথে একটি "পারমিট" যুক্ত করে। যদি অনুমতি পাওয়া যায় তবে পদ্ধতিতে একটি কল park()অবিলম্বে ফিরে আসে, প্রক্রিয়ায় পারমিট গ্রহণ করে। অন্যথায়, এটি ব্লক করে। পদ্ধতিটি কল করলে unparkঅনুমতি পাওয়া যায় যদি এটি এখনও উপলব্ধ না হয়। মাত্র ১টি পারমিট আছে। জাভা ডকুমেন্টেশন ক্লাস LockSupportবোঝায় Semaphore। আসুন একটি সাধারণ উদাহরণ দেখি:

import java.util.concurrent.Semaphore;
public class HelloWorldApp{
    
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Request the permit and wait until we get it
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
এই কোড সবসময় অপেক্ষা করবে, কারণ এখন সেমাফোরের 0 পারমিট আছে। এবং যখন acquire()কোডে কল করা হয় (অর্থাৎ পারমিটের অনুরোধ করুন), থ্রেডটি অনুমতি না পাওয়া পর্যন্ত অপেক্ষা করে। যেহেতু আমরা অপেক্ষা করছি, আমাদের অবশ্যই হ্যান্ডেল করতে হবে InterruptedException। মজার ব্যাপার হল, সেমাফোর একটি আলাদা থ্রেড স্টেট পায়। আমরা যদি JVisualVM এ দেখি, আমরা দেখতে পাব যে রাজ্যটি "অপেক্ষা করুন" নয়, "পার্ক"। একসাথে ভাল: জাভা এবং থ্রেড ক্লাস।  পার্ট II — সিঙ্ক্রোনাইজেশন - 13আসুন আরেকটি উদাহরণ দেখি:

public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            // Park the current thread
            System.err.println("Will be Parked");
            LockSupport.park();
            // As soon as we are unparked, we will start to act
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());
        
        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
থ্রেডের স্ট্যাটাস WAITING হবে, কিন্তু JVisualVM waitকীওয়ার্ড synchronizedএবং parkক্লাসের মধ্যে পার্থক্য করে LockSupport। কেন এই LockSupportএত গুরুত্বপূর্ণ? আমরা আবার জাভা ডকুমেন্টেশনে ফিরে আসি এবং ওয়েটিং থ্রেডের অবস্থা দেখি। আপনি দেখতে পাচ্ছেন, এটিতে প্রবেশ করার জন্য কেবল তিনটি উপায় রয়েছে। এর মধ্যে দুটি উপায় হল wait()এবং join(). এবং তৃতীয়টি হল LockSupport। জাভাতে, লকগুলি LockSupporটি-তেও তৈরি করা যেতে পারে এবং উচ্চ-স্তরের সরঞ্জামগুলি অফার করতে পারে। এর একটি ব্যবহার করার চেষ্টা করা যাক. উদাহরণস্বরূপ, একবার দেখুন ReentrantLock:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
ঠিক আগের উদাহরণগুলির মতো, এখানে সবকিছু সহজ। বস্তুটি lockশেয়ার্ড রিসোর্স প্রকাশ করার জন্য কারো জন্য অপেক্ষা করে। mainআমরা যদি JVisualVM এ দেখি, আমরা দেখতে পাব যে থ্রেডটি লকটি প্রকাশ না করা পর্যন্ত নতুন থ্রেডটি পার্ক করা হবে । আপনি এখানে লক সম্পর্কে আরও পড়তে পারেন: Java 8 StampedLocks বনাম ReadWriteLocks এবং সিঙ্ক্রোনাইজড এবং Java এ লক API। লকগুলি কীভাবে প্রয়োগ করা হয় তা আরও ভালভাবে বোঝার জন্য, এই নিবন্ধে Phaser সম্পর্কে পড়া সহায়ক: Java Phaser-এর নির্দেশিকা । এবং বিভিন্ন সিঙ্ক্রোনাইজার সম্পর্কে বলতে গেলে, আপনাকে অবশ্যই দ্য জাভা সিঙ্ক্রোনাইজারের ডিজোন নিবন্ধটি পড়তে হবে।

উপসংহার

এই পর্যালোচনাতে, আমরা জাভাতে থ্রেডগুলি ইন্টারঅ্যাক্ট করার প্রধান উপায়গুলি পরীক্ষা করেছি। অতিরিক্ত উপাদান: একসাথে ভাল: জাভা এবং থ্রেড ক্লাস। পার্ট I — এক্সিকিউশনের থ্রেডগুলি একসাথে ভাল: জাভা এবং থ্রেড ক্লাস। পার্ট III — মিথস্ক্রিয়া আরও ভাল একসাথে: জাভা এবং থ্রেড ক্লাস। পার্ট IV — কলযোগ্য, ভবিষ্যত এবং বন্ধুরা একসাথে আরও ভাল: জাভা এবং থ্রেড ক্লাস। পার্ট V — এক্সিকিউটর, থ্রেডপুল, ফর্ক/জইন বেটার একসাথে: জাভা এবং থ্রেড ক্লাস। পার্ট VI — আগুন দূরে!
মন্তব্য
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION