CodeGym /وبلاگ جاوا /Random-FA /بهتر با هم: جاوا و کلاس Thread. بخش دوم - همگام سازی
John Squirrels
مرحله
San Francisco

بهتر با هم: جاوا و کلاس Thread. بخش دوم - همگام سازی

در گروه منتشر شد

معرفی

بنابراین، ما می دانیم که جاوا دارای موضوعات است. شما می توانید در مورد آن در بررسی با عنوان Better together: Java and the Thread کلاس بخوانید. بخش اول - موضوعات اجرا . نخ ها برای انجام کار به صورت موازی ضروری هستند. این امر باعث می‌شود که این احتمال وجود داشته باشد که رشته‌ها به نحوی با یکدیگر تعامل داشته باشند. بیایید ببینیم چگونه این اتفاق می افتد و چه ابزارهای اساسی داریم. بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 1

بازده

() Thread.yield مبهم است و به ندرت استفاده می شود. به طرق مختلف در اینترنت توضیح داده شده است. از جمله برخی از افرادی که می نویسند صفی از رشته ها وجود دارد که در آن یک موضوع بر اساس اولویت های رشته پایین می آید. افراد دیگر می نویسند که یک موضوع وضعیت خود را از "در حال اجرا" به "قابل اجرا" تغییر می دهد (حتی اگر هیچ تمایزی بین این وضعیت ها وجود ندارد، یعنی جاوا بین آنها تمایز قائل نمی شود). واقعیت این است که همه چیز بسیار کمتر شناخته شده و در عین حال به یک معنا ساده تر است. بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 2یک اشکال ( JDK-6416721: (رشته مشخصات) Fix Thread.yield() javadoc ) برای yield()مستندات روش ثبت شده است. اگر آن را بخوانید، واضح است که این yield()روش در واقع فقط توصیه‌هایی را به زمان‌بندی رشته جاوا ارائه می‌کند که می‌توان زمان اجرای کمتری به این رشته داد. اما اینکه واقعاً چه اتفاقی می‌افتد، یعنی اینکه آیا زمان‌بند به توصیه‌ها عمل می‌کند و به طور کلی چه کاری انجام می‌دهد، به پیاده‌سازی JVM و سیستم عامل بستگی دارد. و ممکن است به عوامل دیگری نیز بستگی داشته باشد. همه سردرگمی ها به احتمال زیاد به این دلیل است که همزمان با توسعه زبان جاوا، multithreading تجدید نظر شده است. اطلاعات بیشتر را در نمای کلی اینجا بخوانید: معرفی مختصر جاوا () Thread.yield .

خواب

یک نخ می تواند در حین اجرای خود به خواب برود. این ساده ترین نوع تعامل با موضوعات دیگر است. سیستم عاملی که ماشین مجازی جاوا را اجرا می کند که کد جاوا ما را اجرا می کند، زمانبندی رشته خود را دارد . این تصمیم می گیرد که کدام رشته و چه زمانی شروع شود. یک برنامه نویس نمی تواند مستقیماً از طریق کد جاوا و فقط از طریق JVM با این زمانبند تعامل داشته باشد. او می‌تواند از برنامه‌ریز بخواهد که موضوع را برای مدتی متوقف کند، یعنی آن را به خواب ببرد. می‌توانید در این مقالات بیشتر بخوانید: Thread.sleep() و Multithreading چگونه کار می‌کند . همچنین می‌توانید نحوه عملکرد رشته‌ها در سیستم‌عامل‌های ویندوز را بررسی کنید: Internals of Windows Thread . و حالا بیایید آن را با چشمان خود ببینیم. کد زیر را در فایلی به نام ذخیره کنید 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 برای دریافت PID (شناسه فرآیند) استفاده می کنیم و لیست رشته ها را با " باز می کنیم jvisualvm --openpid pid: بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 3همانطور که می بینید، موضوع ما اکنون وضعیت "Sleeping" را دارد. در واقع، یک راه زیباتر برای کمک وجود دارد. تاپیک ما رویاهای شیرین می بیند:
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()متد را در thread فراخوانی کردیم. این روش یک پرچم داخلی به نام "وضعیت وقفه" تنظیم می کند. یعنی هر رشته دارای یک پرچم داخلی است که مستقیماً قابل دسترسی نیست. اما ما روش های بومی برای تعامل با این پرچم داریم. اما این تنها راه نیست. یک رشته ممکن است در حال اجرا باشد، منتظر چیزی نباشد، به سادگی اقداماتی را انجام دهد. اما ممکن است پیش بینی کند که دیگران بخواهند کار خود را در یک زمان خاص به پایان برسانند. مثلا:
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حلقه تا زمانی که thread به صورت خارجی قطع شود اجرا می شود. در مورد isInterruptedپرچم، مهم است که بدانیم اگر یک را بگیریم InterruptedException، پرچم isInterrupted دوباره تنظیم می شود و سپس isInterrupted()false برمی گردد. کلاس Thread همچنین دارای یک متد ثابت () Thread.interrupted است که فقط برای رشته فعلی اعمال می شود، اما این متد پرچم را به false بازنشانی می کند! در این فصل با عنوان قطع موضوع بیشتر بخوانید .

بپیوندید (منتظر بمانید تا موضوع دیگری تمام شود)

ساده ترین نوع انتظار، انتظار برای پایان یافتن یک رشته دیگر است.
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 ثانیه می‌خوابد. در عین حال نخ اصلی منتظر می ماند تا نخ خواب بیدار شود و کار خود را تمام کند. اگر به وضعیت thread در JVisualVM نگاه کنید، به این صورت خواهد بود: بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 4به لطف ابزارهای نظارتی، می توانید ببینید که در مورد موضوع چه می گذرد. این joinروش بسیار ساده است، زیرا فقط یک متد با کد جاوا است که wait()تا زمانی که رشته ای که روی آن فراخوانی می شود زنده است اجرا می شود. به محض از بین رفتن نخ (زمانی که کارش تمام شد) انتظار قطع می شود. و این همه جادوی روش است join(). بنابراین، بیایید به جالب ترین چیز برویم.

نظارت کنید

Multithreading شامل مفهوم مانیتور می شود. کلمه مانیتور از طریق زبان لاتین قرن شانزدهم به انگلیسی آمده است و به معنای "ابزار یا وسیله ای است که برای مشاهده، بررسی یا ثبت یک پرونده مداوم از یک فرآیند استفاده می شود". در چارچوب این مقاله سعی خواهیم کرد به اصول اولیه بپردازیم. برای هر کسی که جزئیات را می خواهد، لطفاً به مطالب مرتبط شیرجه بزنید. ما سفر خود را با مشخصات زبان جاوا (JLS) آغاز می کنیم: 17.1. هماهنگ سازی . این می گوید: بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 5به نظر می رسد که جاوا از مکانیزم "مانیتور" برای همگام سازی بین رشته ها استفاده می کند. یک مانیتور با هر شی مرتبط است و رشته ها می توانند آن را با lock()یا رها کنند unlock(). در مرحله بعد، آموزش را در وب‌سایت اوراکل خواهیم یافت: قفل‌های درونی و همگام‌سازی . این آموزش می گوید که همگام سازی جاوا حول یک موجودیت داخلی به نام قفل ذاتی یا قفل مانیتور ساخته شده است . این قفل اغلب به سادگی " مانیتور " نامیده می شود. همچنین می بینیم که هر شی در جاوا دارای یک قفل ذاتی مرتبط با آن است. می توانید Java - Intrinsic Locks and Synchronization را بخوانید . در مرحله بعد مهم است که بفهمیم چگونه یک شی در جاوا می تواند با یک مانیتور مرتبط شود. در جاوا، هر شیء دارای یک هدر است که ابرداده داخلی را که از طریق کد در دسترس برنامه نویس نیست، ذخیره می کند، اما ماشین مجازی برای کار صحیح با اشیا به آن نیاز دارد. هدر شی شامل یک کلمه علامت گذاری است که به شکل زیر است: بهتر با هم: جاوا و کلاس Thread.  قسمت دوم - همگام سازی - 6

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

در اینجا یک مقاله JavaWorld است که بسیار مفید است: چگونه ماشین مجازی جاوا همگام سازی رشته را انجام می دهد . این مقاله باید با توضیحات بخش "خلاصه" موضوع زیر در سیستم ردیابی اشکال JDK ترکیب شود: JDK-8183909 . شما می توانید همین مطلب را در اینجا بخوانید: JEP-8183909 . بنابراین، در جاوا، یک مانیتور با یک شی مرتبط است و برای مسدود کردن یک رشته زمانی که thread تلاش می کند قفل را بدست آورد (یا دریافت کند) استفاده می شود. در اینجا ساده ترین مثال است:
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) گفته شد، این وضعیت به فضایی در ناحیه Native Heap حافظه نیاز دارد تا این موجودیت ذخیره شود. ارجاع به محل حافظه این موجودیت داخلی در کلمه علامت سربرگ شی ذخیره می شود. بنابراین، یک مانیتور در واقع مکانیزمی برای همگام سازی دسترسی به منابع مشترک در بین موضوعات متعدد است. JVM بین چندین پیاده سازی این مکانیسم سوئیچ می کند. بنابراین، برای سادگی، وقتی در مورد مانیتور صحبت می کنیم، در واقع در مورد قفل صحبت می کنیم. بهتر با هم: جاوا و کلاس Thread.  قسمت دوم - همگام سازی - 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(" ...");
	}
}
در اینجا thread اصلی ابتدا شی وظیفه را به thread جدید منتقل می کند و سپس بلافاصله قفل را می گیرد و یک عملیات طولانی با آن انجام می دهد (8 ثانیه). در تمام این مدت، کار نمی تواند ادامه یابد، زیرا نمی تواند وارد بلوک شود synchronized، زیرا قفل قبلاً بدست آمده است. اگر نخ نتواند قفل را بگیرد، منتظر مانیتور می ماند. به محض اینکه قفل را دریافت کرد، اجرا را ادامه خواهد داد. هنگامی که یک نخ از یک مانیتور خارج می شود، قفل را آزاد می کند. در JVisualVM، به این صورت است: بهتر با هم: جاوا و کلاس Thread.  قسمت دوم - همگام سازی - 8همانطور که در JVisualVM می بینید، وضعیت "Monitor" است، به این معنی که موضوع مسدود شده است و نمی تواند مانیتور را بگیرد. همچنین می‌توانید از کد برای تعیین وضعیت یک رشته استفاده کنید، اما نام‌های وضعیتی که از این طریق تعیین می‌شوند با نام‌های استفاده شده در JVisualVM مطابقت ندارند، اگرچه مشابه هستند. در این حالت، عبارت در حلقه for BLOCKEDth1.getState() برمی‌گردد ، زیرا تا زمانی که حلقه در حال اجرا است، مانیتور شی توسط thread اشغال می‌شود و thread مسدود می‌شود و تا زمانی که قفل آزاد نشود نمی‌تواند ادامه یابد. علاوه بر بلوک های همگام، یک روش کامل را می توان همگام کرد. به عنوان مثال، در اینجا یک متد از کلاس آمده است: lockmainth1HashTable
public synchronized int size() {
	return count;
}
این متد در هر لحظه فقط توسط یک رشته اجرا می شود. آیا واقعاً به قفل نیاز داریم؟ بله، ما به آن نیاز داریم. در مورد روش های نمونه، شی "این" (شیء فعلی) به عنوان یک قفل عمل می کند. بحث جالبی در مورد این موضوع در اینجا وجود دارد: آیا استفاده از روش همگام به جای بلوک همگام مزیتی دارد؟ . اگر متد ثابت باشد، قفل شیء "this" نخواهد بود (زیرا شیء "this" برای یک متد استاتیک وجود ندارد)، بلکه یک شی Class خواهد بود (مثلاً) Integer.class.

صبر کنید (در انتظار یک مانیتور). متدهای notify() و notifyAll()

کلاس Thread روش انتظار دیگری دارد که با یک مانیتور مرتبط است. بر خلاف 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، به این صورت است: بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 10برای درک اینکه چگونه این کار می کند، به یاد داشته باشید که wait()و notify()متدها با java.lang.Object. ممکن است عجیب به نظر برسد که متدهای مرتبط با رشته در Objectکلاس هستند. اما دلیل آن اکنون آشکار می شود. شما به یاد خواهید آورد که هر شی در جاوا یک هدر دارد. هدر حاوی اطلاعات مختلف خانه داری از جمله اطلاعات مربوط به مانیتور یعنی وضعیت قفل است. به یاد داشته باشید، هر شی یا نمونه ای از یک کلاس، با یک موجودیت داخلی در JVM، به نام قفل یا مانیتور ذاتی مرتبط است. در مثال بالا، کد مربوط به شیء وظیفه نشان می دهد که ما بلوک همگام سازی شده برای نمایشگر مرتبط با شی را وارد می کنیم lock. اگر موفق شدیم قفل این مانیتور را بدست آوریم، wait()نامیده می شود. رشته ای که وظیفه را اجرا می کند، lockمانیتور شی را آزاد می کند، اما وارد صف رشته هایی می شود که منتظر اطلاع رسانی از lockمانیتور شی هستند. این صف از رشته ها، مجموعه WAIT نامیده می شود که هدف آن را به درستی نشان می دهد. یعنی بیشتر یک مجموعه است تا یک صف. thread mainیک رشته جدید با شی task ایجاد می کند، آن را شروع می کند و 3 ثانیه صبر می کند. این باعث می‌شود که احتمال اینکه رشته جدید بتواند قفل قبل از mainنخ را بگیرد و وارد صف نمایشگر شود بسیار زیاد است. پس از آن، mainنخ خود وارد lockبلوک همگام شیء می شود و با استفاده از مانیتور، اعلان رشته را انجام می دهد. پس از ارسال اعلان، mainthread مانیتور شی را آزاد می کند lockو رشته جدید که قبلاً منتظر lockرها شدن مانیتور شی بود، به اجرا ادامه می دهد. ارسال اعلان فقط به یک رشته ( notify()) یا به طور همزمان به تمام رشته های موجود در صف ( notifyAll()) امکان پذیر است. اینجا بیشتر بخوانید: تفاوت بین notify() و notifyAll() در جاوا . توجه به این نکته مهم است که ترتیب اطلاع رسانی به نحوه پیاده سازی JVM بستگی دارد. اینجا بیشتر بخوانید: چگونه با notify و notifyAll گرسنگی را حل کنیم؟ . همگام سازی را می توان بدون تعیین یک شی انجام داد. شما می توانید این کار را زمانی انجام دهید که کل یک روش به جای یک بلوک کد، همگام سازی شده باشد. به عنوان مثال، برای متدهای استاتیک، قفل یک شی Class خواهد بود (از طریق .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()یک بازه زمانی تعیین کرده باشد. بهتر با هم: جاوا و کلاس Thread.  بخش دوم - همگام سازی - 11

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

چرخه عمر نخ

در طول عمر آن، وضعیت یک رشته تغییر می کند. در واقع این تغییرات چرخه عمر نخ را تشکیل می دهند. به محض ایجاد یک موضوع، وضعیت آن NEW است. در این حالت، موضوع جدید هنوز در حال اجرا نیست و زمانبندی رشته جاوا هنوز چیزی در مورد آن نمی داند. برای اینکه زمان‌بندی رشته در مورد موضوع اطلاعات کسب کند، باید thread.start()متد را فراخوانی کنید. سپس thread به حالت RUNNABLE منتقل می شود. اینترنت نمودارهای نادرست زیادی دارد که بین حالت های "قابل اجرا" و "در حال اجرا" تمایز قائل می شود. اما این یک اشتباه است، زیرا جاوا بین "آماده کار" (قابل اجرا) و "کار" (در حال اجرا) تفاوتی قائل نمی شود. وقتی رشته ای زنده است اما فعال نیست (قابل اجرا نیست)، در یکی از دو حالت است:
  • BLOCKED - در انتظار ورود به بخش مهم، یعنی یک synchronizedبلوک.
  • WAITING - انتظار برای یک رشته دیگر برای برآوردن برخی شرایط.
اگر شرط برآورده شود، زمانبندی رشته موضوع را شروع می کند. اگر رشته تا زمان مشخصی منتظر باشد، وضعیت آن TIMED_WAITING است. اگر موضوع دیگر در حال اجرا نیست (تمام شده است یا یک استثنا ایجاد شده است)، سپس وارد وضعیت TERMINATED می شود. برای اطلاع از وضعیت یک نخ، از getState()روش استفاده کنید. Thread ها همچنین دارای isAlive()متدی هستند که اگر موضوع TERMINATED نباشد مقدار true را برمی گرداند.

LockSupport و پارکینگ نخ

با شروع جاوا 1.6، مکانیزم جالبی به نام LockSupport ظاهر شد. بهتر با هم: جاوا و کلاس Thread.  قسمت دوم - همگام سازی - 12این کلاس با هر رشته ای که از آن استفاده می کند یک "مجوز" مرتبط می کند. در صورت در دسترس بودن مجوز ، یک فراخوانی به park()متد بلافاصله برمی‌گردد و مجوز را در فرآیند مصرف می‌کند. در غیر این صورت مسدود می شود. فراخوانی unparkروش باعث می شود مجوز در صورتی که هنوز در دسترس نباشد در دسترس باشد. فقط 1 مجوز وجود دارد. مستندات جاوا برای 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 نگاه کنیم، می بینیم که حالت "Wait" نیست، بلکه "Park" است. بهتر با هم: جاوا و کلاس Thread.  قسمت دوم - همگام سازی - 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اینقدر مهم است؟ ما دوباره به مستندات جاوا می پردازیم و به وضعیت رشته WAITING نگاه می کنیم . همانطور که می بینید، تنها سه راه برای ورود به آن وجود دارد. دو تا از آن راه ها wait()و join(). و سومی این است LockSupport. در جاوا، قفل ها همچنین می توانند بر روی t ساخته شوند 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منتظر می ماند تا کسی منبع مشترک را آزاد کند. اگر به JVisualVM نگاه کنیم، خواهیم دید که رشته جدید تا زمانی که thread mainقفل آن را آزاد نکند، پارک خواهد شد. می توانید اطلاعات بیشتری درباره قفل ها در اینجا بخوانید: Java 8 StampedLocks در مقابل ReadWriteLocks و Synchronized and Lock API در جاوا. برای درک بهتر نحوه پیاده‌سازی قفل‌ها، خواندن در مورد Phaser در این مقاله مفید است: راهنمای Java Phaser . و در مورد همگام‌کننده‌های مختلف، باید مقاله DZone در مورد همگام‌سازهای جاوا را بخوانید.

نتیجه

در این بررسی، روش‌های اصلی تعامل رشته‌ها در جاوا را بررسی کردیم. مواد اضافی: بهتر با هم: جاوا و کلاس Thread. قسمت اول - موضوعات اجرا بهتر با هم: جاوا و کلاس Thread. بخش سوم - تعامل بهتر با هم: کلاس جاوا و Thread. بخش چهارم - Callable، Future، و دوستان بهتر با هم: Java and the Thread کلاس. قسمت پنجم - Executor، ThreadPool، Fork/Join Better together: Java و کلاس Thread. قسمت ششم - آتش دور شوید!
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION