پرش به محتوای اصلی
مسیر تخصصی جاوا و Spring
درخواست پروژهتدریس خصوصیارتباط با ماراهنما
جاواپرو v1.2.34یاد بگیر، تمرین کن، حرفه‌ای بساز
خانهدوره‌هاآموزش رایگانوبلاگمدرس‌هاخدمات
خانهکلید واژه ی volatile در زبان برنامه نویسی جاوا
وبلاگ »

کلید واژه ی volatile در زبان برنامه نویسی جاوا

Volatile در زبان برنامه نویسی جاوا چیست؟ چه موقع از آن استفاده کنیم؟ در صورتی که استفاده نکنیم باید منتظر چه عواقبی باشیم؟

۷ دقیقه مطالعهوبلاگ »آموزش جاوا و انجام پروژه های برنامه نویسی
کلید واژه ی volatile در زبان برنامه نویسی جاوا
محتوای منتخب جاواپرو

خلاصه مطلب

Volatile در زبان برنامه نویسی جاوا چیست؟ چه موقع از آن استفاده کنیم؟ در صورتی که استفاده نکنیم باید منتظر چه عواقبی باشیم؟
نوع محتوامقاله آموزشی
موضوعوبلاگ »
زمان مطالعه۷ دقیقه مطالعه
آخرین ویرایش۳۰ اردیبهشت ۱۴۰۴

لیست دوره های آکادمی جاوا پرو

دوره پرتاب | آموزش پیش نیازهای قبل از شروع برنامه نویسیدوره مقدماتی زبان برنامه نویسی جاوادوره جامع شاهکار پیشرفته جاوادوره جامع طلایی Spring Coreدوره آموزش فریمورک Spring Bootدوره پروژه محور Spring Boot - سیستم دانشگاهدوره جامع دژبان Spring securityدوره جامع نخبگان معماری میکروسرویس با Java و Spring Boot

فرض کنید دو Thread دارید . یکی از این نخ ها مقداری را تغییر میدهد و دیگری بر مبنای تغییر داده شد دستوراتی اجرا میکند. کد زیر را در نظر بگیرید :

public class NoVisibility { private static boolean ready=false; private static int number=0; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) throws InterruptedException { new ReaderThread().start(); number = 42; ready = true; } }

نکته : متد yield برای رها کردن پردازنده مورد استفاده قرار میگیرد. درواقع برای مدت کوتاهی پردازنده رها شده و در اولین فرصت مجددا به ترد اختصاص میابد.

هیچ تضمینی وجود ندارد که کد فوق خروجی 42 تولید کند (البته احتمالا اگر آن را اجرا کنید چنین خروحی ای مشاهده می کنید . شاید هم نه). عدم اطمینان از خروجی ای که بدیهی بنظر میرسد از کجا ناشی میشود؟

میدانیم که برنامه های چند نخی درواقع به دو صورت اجرا خواهند شد : 1- به صورت همروند 2- به صورت موازی

در همروند توازی واقعی نداریم . تنها در صورتی توازی واقعی ممکن است داشته باشیم که تعداد هسته ها بیش از یک باشد.

آموزش چند نخی(Multithreading) در زبان برنامه نویسی جاوا[کلیک کنید] : در دوره آموزش پیشرفته جاوا در فصل 12 به مباحث چندنخی (تردها) در زبان برنامه نویسی جاوا پرداخته ایم.

خطای دید (Visibility Problem):

Parallel
Parallel

اگر نخست ترد Main بتواند مقدار ready را عوض کند و سپس ReaderThread این مقدار را بخواند. ترد دوم در حلقه ی بی نهایت گیر نمی افتد .در غیر اینصورت ممکن است ReaderThread همواره اجرا گردد. در صورتی که Main Thread نتواند در ابتدا مقدار ready را true کند , ترد دوم مقدار Ready که ابتدا false بوده را در رجیستر های هسته ذخیره میکند و تغییراتی که Main Thread بر روی ready اعمال میکند بر رجیستر های هسته ای که ReaderThread را اجرا میکند منعکس نمیشود. بنابراین دو متغیر Ready داریم یکی در رجیستر های پردازنده اول که مربوط به Main Thread است و دیگری در پردازنده ی دوم که مربوط به ReaderThread است.این اتفاق تحت عنوان Reordering شناخته میشود(یعنی دستورات به ترتیبی که نوشتیم و انتظار داریم اجرا نمیشوند) . درواقع تضمینی وجود ندارد که چه زمانی کدام Thread مقدار Ready را میخواند. درواقع ترد دوم ممکن است مقداری که Stale (کهنه) است را ببیند.مشکلی که نخ ها نمیتوانند اخرین مقدار یک متغیر را ببینند Visibility Problem نامیده میشود.

وقتی یک نخ ، متغیری را بدون همگام سازی میخواند ، ممکن است مقداری کهنه (Stale) ببیند. اما حداقل مقداری را میبند که توسط یک نخ دیگر ذخیره شده و صرفا بروز نیست . درواقع مقداری تصادفی نمیبند . چنین ضمانتی ، ضمانت out-of-thin-air نامیده میشود.

چنین ضمانتی یک استثنا دارد و آن هم متغیر های 64 بیت هستند(long , double). عملیات های خواندن و نوشتن در مقادیر 64 بیتی که volatile نیستند بصورت دو خواندن و نوشتن مجزای 32 بیتی انجام میشوند(در زمانی که جاوا نوشته شد بیشتر پردازنده ها بصورت غیر بهینه عملیات بر روی متغیر های 64 بیتی را ارائه میدادند). در این حین اگر دو ترد داشته باشیم که یکی میخواند و دیگری مینویسد ، ممکن است عملیات نوشتن در 32 بیت اول انجام شود و خواندن در همین حین انجام شده و سپس 32 بیت بعدی نوشته شوند در اینصورت مقداری مشاهده خواهیم کرد که کاملا غیر منتظره و گویی تصادفی است.

اتمیک بودن (Atomicity)

عملیات اتمیک یعنی عملیاتی که بدون دستکاری نخ های دیگر انجام شود.

متغیر های Volatile

با اعلان یک متغیر بصورت volatile به کامپایلر میگوییم که از reordering جلوگیری کن. کامپایلر بدین صورت عمل میکند که اگر هر دو عملیات read و write برای یک متغیر ببیند ابتدا عملیات write و سپس read را انجام میدهد که منجر به جلوگیری از داده های Stale (کهنه ) میشود.

درواقع متغیر های volatile صرفا برای Visibility Problem استفاده میشوند.

محدودیت های متغیر های Volatile

متغیر های Volatile به اندازه ی کافی قوی نیستند که یک increment operation(count++ مثلا ) را بصورت Atomic پیاده سازی کنند مگر اینکه تضمین شود نوشتن متغیر صرفا توسط یک نخ انجام میپذیرد. راه حل : استفاده از Synchronized.

Synchronized میتواند هم visibility و هم atomicity را گارانتی کند درحالی که volatile صرفا visibility را گارانتی میکند.

چرا متغیر های Volatile در زمینه ی Increment operation ضعیف هستند؟

متاسفانه volatile صرفا تضمین میکند عملیات read قبل از write صورت بگیرد . مثلا اگر دو نخ همزمان اقدام به افزایش یک واحدی یک متغیر کنند ،بر خلاف انتظار متغیر ممکن است صرفا یک واحد افزایش پیدا کند. در چنین شرایطی نیاز به تضمین اتمیک بودن(Atomicity) عملیات خواهد بود.

چه زمانی از متغیر های volatile استفاده کنیم ؟

فرض کنید a=1 و دو نخ میخواهند a++ را اجرا کنند .اگر دقیقا همزمان a افزایش دهند هر دو در ابتدا مقدار a=1 را مشاهده و سپس a++ میکنند یعنی در اخر a=2:

ترد A مقدار a را میبند(که یک است)

ترد B مقدار a را میبند(که یک است)

ترد A مقدار a را با یک جمع میکند و حاصل را در a قرار میدهد(که دو میشود)

ترد B مقدار a را با یک جمع میکنند و حاصل را در a قرار میدهد(که دو میشود)

در شرایط بالا میتوانیم از کلاس های اماده جاوا مثل AtomicInteger که عملیات خواندن نوشتن و تغییر دادن متغیر را بصورت اتمیک ارائه میدهند استفاده کنیم(در مقاله ای جدا به اینکه چطور همچین عملیاتی را بصورت اتمیک ارائه میدهند خواهیم پرداخت).

برای اینکه بتوانیم شرایطی را فراهم اوریم که یک متغیر volatile بتواند increment شود(در حالت کلی بتواند به مقدار خودش وابسته باشد و بر مبنای ان تغییر یابد) بدون اینکه نخ ها را محدود کنیم ، بایستی کاری کنیم که عملیات خواندن مقدار متغیر و افزایش ان بصورت Atomic صورت بگیرد. یعنی مثلا اگر نخ A مقدار a را یک دید ، بصورت متوالی و Atomic افزایش هم انجام شود در این صورت ترد B مقدار 2 را میبیند.برای این منظور از الگوریتمی به نام Compare and Swap استفاده میکنیم که در کلاس های Atomic جاوا مثل AtomicInteger از همین الگوریتم استفاده میشود.

Compare and Swap

برای اینکه بتوانیم عملیات Read و Write را بصورت Atomic انجام دهیم از چنین الگوریتمی استفاده میکنیم. مثلا فرض کنید میخواهیم عملیات increment را بصورت Atomic پیاده سازی کنیم :

class EmulatedCAS { private int value; public synchronized int getValue() { return value; } public synchronized int compareAndSwap(int expectedValue, int newValue) { int readValue = value; if (readValue == expectedValue) value = newValue; return readValue; } }class Counter { private EmulatedCAS value = new EmulatedCAS(); public int getValue() { return value.getValue(); } public int increment() { int readValue = value.getValue(); while (value.compareAndSwap(readValue, readValue + 1) != readValue) readValue = value.getValue(); return readValue + 1; } }

همانطور که مشاهده میکنید موقعی که میخواهیم مقدار متغیر را تغییر دهیم . عملیات خواندن و نوشتن درون بلاک synchronized انجام میپذیرد و این یعنی در زمان خواندن و قبل از تغییر دادن مقدار هیچ نخ دیگری نمیتواند بخواند پس مشکلات ذکر شده دیگر رخ نمیدهند.

منابع

برچسب‌هاوبلاگ »مقالات جاوا »Volatile چیستچه زمانی از volatile استفاده کنیمVisibility problem چیستAtomicity چیستموارد استفاده volatileکاربرد volatilevolatile در جاواآموزش volatile در جاواآموزش volatileکاربرد volatile در جاواجاوازبان برنامه نویسی جاوا
ادامه مسیر

پیشنهاد جاواپرو

موضوع را با آموزش‌های مرتبط ادامه دهید و نکته‌های کاربردی را در مسیر خود نگه دارید

مشاهده دوره‌ها
مطالب پیشنهادی

ادامه مطالعه

xدسته‌بندی نشده
۱۱ دی ۱۳۹۸ مطالعه کوتاه

x

پادکست شماره 3 – راه های کسب درآمد از برنامه نویسی

مطالعه مطلب
پادکست شماره 3 - راه های کسب درآمد از برنامه نویسیپادکست
۲۹ اردیبهشت ۱۴۰۴ پادکست آموزشی

پادکست شماره 3 - راه های کسب درآمد از برنامه نویسی

در این پادکست برنامه نویسی به شیوه ها و راه های کسب درآمد از برنامه نویسی پرداخته ایم. یک کاغذ و قلم بردارید و به پادکست گوش بدید.

شنیدن پادکست
پادکست شماره 2 - چگونه وارد فضای برنامه نویسی شویم؟پادکست
۲۹ اردیبهشت ۱۴۰۴ پادکست آموزشی

پادکست شماره 2 - چگونه وارد فضای برنامه نویسی شویم؟

در این پادکست برنامه نویسی یاد میگیریم که چگونه وارد فضای برنامه نویسی شویم و تخصص برنامه نویسی را به دست آوریم.

شنیدن پادکست
جاواپرومرجع تخصصی یادگیری و توسعهٔ جاوا

تجربه‌ای منظم، ساده و حرفه‌ای برای یادگیری عمیق جاوا، Spring و مهندسی نرم‌افزار؛ قابل استفاده در موبایل، تبلت و دسکتاپ

یادگیری

همه دوره‌هاآموزش رایگانمسیر یادگیریمدرس‌ها

جاواپرو

درباره ماخدمات تخصصیدرخواست پروژهتدریس خصوصیبلاگ فنیرپورتاژ آگهی

پشتیبانی

تماس با ماپرسش‌های متداولقوانین و حریم خصوصیبروزرسانی‌های جاواپروثبت تیکت

مجوزها

نماد اعتمادنماد اعتماد
خرید امنپرداخت فقط در درگاه رسمیپشتیبانی آموزشیمسیر ارتباط شفاف و پیگیری‌پذیردسترسی پایداروب‌اپ واکنش‌گرا روی همه دستگاه‌ها
© ۱۴۰۵ جاواپرو؛ همه حقوق محفوظ استتوسعه توسط پایاپینگ
خانهدوره‌هاسبد