جاوا
جلسه ۷۴: تفاوت برنامه و پروسه و نخ در جاوا

۱٫ int counter = 0;
۲٫
۳٫ void incrementCounter() {
۴٫ counter++;
۵٫ }
دستور افزایش متغیر counter
در خط ۴ به مراحلی مشابه مراحل زیر در کامپیوتر تجزیه می شود:- مقدار متغیر
counter
را از رجیستری (حافظه موقتی سریع) که در آن ذخیره شده است بخوان - یک واحد به مقداری که خوانده شده است اضافه کن
- نتیجه محاسبات فوق را دوباره در رجیستر ذخیره کن
incrementCounter
در قطعه کد بالا را اجرا کنند ، یکی از راه های محتمل برای اجرای دو نخ به شرح زیر است:یکی از نخ ها را T1
و دیگری را T2
می نامیم. فرض کنید مقدار شمارنده برابر با ۷ باشد.- اجرای
T1
در حال حاضر رویCPU
برنامه ریزی شده و وارد تابع می شود. مرحله A را انجام می دهد یعنی مقدار متغیر را از رجیستر می خواند که ۷ است. - سیستم عامل تصمیم می گیرد پروسه اجرایی را تغییر دهد و
T2
را وارد مرحله اجرایی کرده وT1
را از اجرا خارج کند. T2
وارد فاز اجرا می شود و از شانس خوبش قبل از تحویل فاز اجرایی بهT1
تمام سه مرحلهB
،A
وC
را انجام می دهد. مقدار ۷ را می خواند ، یک عدد به آن اضافه می کند و عدد ۸ را ذخیره می کند.T1
دوباره برمی گردد و از آنجا که وضعیت آن توسط سیستم عامل ذخیره شده است ، هنوز هم مقدار قدیمی متغیرcounter
یعنی ۷ را استفاده می کند این مقداری است که قبل از تغییر پروسه خوانده شده بود. نخT1
نمی داند که پشت سرش مقدار متغیر به روز شده است. متأسفانه فکر می کند که مقدار هنوز ۷ است ، یک عدد به آن اضافه می کند و حاصلجمع حاصل یعنی ۸ را بجای مقدارcounter
(که از قبل ۸ بود) می نویسد. اگر نخ ها به صورت سریالی پشت سر هم اجرا می شدند، حالا مقدار نهایی ۹ بود.
کلاس Thread unsafe
یک دقیقه وقت بگذارید و برنامه زیر را بررسی کنید. این برنامه یک شمارنده را به تعداد دفعات یکسان افزایش و کاهش می دهد. مقدار نهایی شمارنده باید صفر باشد ، با این حال ، اگر برنامه را به تعداد بار کافی اجرا کنید ، گاهی اوقات نتیجه مورد انتظار صفر را دریافت می کنید و در برخی دیگر از مواقع ، مقدار غیر صفر را دریافت می کنید. در این برنامه نخ ها را به مدتی تصادفی sleep
می کنیم یا می خوابانیم ، تا امکانی به نخ ها بدهیم که به ترتیبی غیر قطعی اجرا شوند.import java.util.Random;
class DemoThreadUnsafe {
// We'll use this to randomly sleep our threads
static Random random = new Random(System.currentTimeMillis());
public static void main(String args[]) throws InterruptedException {
// create object of unsafe counter
ThreadUnsafeCounter badCounter = new ThreadUnsafeCounter();
// setup thread1 to increment the badCounter 200 times
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
badCounter.increment();
DemoThreadUnsafe.sleepRandomlyForLessThan10Secs();
}
}
});
// setup thread2 to decrement the badCounter 200 times
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
badCounter.decrement();
DemoThreadUnsafe.sleepRandomlyForLessThan10Secs();
}
}
});
// run both threads
thread1.start();
thread2.start();
// wait for t1 and t2 to complete.
thread1.join();
thread2.join();
// print final value of counter
badCounter.printFinalCounterValue();
}
public static void sleepRandomlyForLessThan10Secs() {
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException ie) {
}
}
}
class ThreadUnsafeCounter {
int count = 0;
public void increment() {
count++;
}
public void decrement() {
count--;
}
void printFinalCounterValue() {
System.out.println("counter is: " + count);
}
}
این جلسه تفاوت بین برنامه ، پروسه و نخ را بررسی می کند. همچنین نمونه ای از یک برنامه thread-unsafe
(چند نخی ناایمن) را می بینید.موارد زیر را بیان خواهیم کرد:- برنامه
- پروسه
- نخ
- هشدارها
- برنامه شمارنده
- کلاس
Thread unsafe
برنامه
یک برنامه مجموعه ای از دستورالعمل ها و داده های مرتبط است که روی دیسک قرار دارد و برای انجام برخی کارها توسط سیستم عامل بارگیری می شود. یک فایل اجرایی یا یک اسکریپت پایتون نمونه ای از برنامه ها هستند. به منظور اجرای یک برنامه ، ابتدا از هسته سیستم عامل درخواست ایجاد یک پروسه جدید می شود. پروسه محیطی برای اجرای برنامه است.پروسه
پروسه یک برنامه در حال اجرا است. پروسه یک محیط اجرا است که شامل دستورالعمل ها ، داده های کاربر و بخش داده های سیستم و همچنین بسیاری از منابع دیگر مانندCPU
، حافظه ، فضای آدرس ، دیسک و I/O
شبکه است که در زمان اجرا اختصاص می یابد. چندین پروسه از یک برنامه می تواند به صورت همزمان درحال اجرا باشد اما یک پروسه لزوما فقط به یک برنامه تعلق دارد.نخ
نخ (Thread) کوچکترین واحد اجرا در یک پروسه (process) است. یک نخ به سادگی دستورات را به طور متوالی اجرا می کند. هر پروسه می تواند چندین نخ داشته باشد که به عنوان بخشی از آن پروسه اجرا می شوند. معمولاً برخی از حالتها یا متغیرهای مرتبط با پروسه وجود دارد که با تمام نخ ها به اشتراک گذاشته می شود بعلاوه هر نخ می تواند برخی از متغیرها یا حالتهای خصوصی داشته باشد. متغیرهای مشترک بین نخ های یک پروسه برای همه نخ های آن پروسه قابل مشاهده و در دسترس است و هنگامی که هر نخی این متغیر مشترک را بخواند یا تغییردهد ، باید دقت شود. چندین ساختار توسط زبانهای برنامه نویسی مختلف برای محافظت و نظم در دسترسی به این متغیرهای مشترک ارائه شده است که در جلسه های آینده بیشتر به آنها خواهیم پرداخت.اصطلاح ها
توجه داشته باشید که اصطلاح برنامه و پروسه اغلب به جای هم استفاده می شوند اما بیشتر اوقات منظور پروسه است.همچنین مفهوم سیستم های “پردازش موازی” وجود دارد که در آن چندین پروسه در بیش از یکCPU
پردازش می شوند. معمولاً این امر به پشتیبانی سخت افزاری نیاز دارد که یک سیستم واحد دارای چندین هسته پردازنده باشد یا اجرای برنامه در گروهی از ماشین ها انجام شود. پروسه ها هیچ منابعی را بین خود به اشتراک نمی گذارند در حالیکه نخ های یک پروسه می توانند منابع اختصاص یافته به آن پروسه خاص ، از جمله فضای آدرس حافظه را به اشتراک بگذارند. با این حال ، زبانها امکاناتی را فراهم می کنند تا ارتباط بین پروسه ها را امکان پذیر سازند.برنامه شمارنده
در زیر مثالی آورده شده است که نشان می دهد که هنگام تغییر داده مشترک توسط چند نخ ، احتیاط لازم است. درصورت همگام سازی نادرست بین نخ ها بسته به اینکه نخ ها به چه ترتیبی اجرا می شوند ، خروجی برنامه را متفاوت می کند.قطعه کد زیر را در نظر بگیرید:۱٫ int counter = 0;
۲٫
۳٫ void incrementCounter() {
۴٫ counter++;
۵٫ }
دستور افزایش متغیر counter
در خط ۴ به مراحلی مشابه مراحل زیر در کامپیوتر تجزیه می شود:- مقدار متغیر
counter
را از رجیستری (حافظه موقتی سریع) که در آن ذخیره شده است بخوان - یک واحد به مقداری که خوانده شده است اضافه کن
- نتیجه محاسبات فوق را دوباره در رجیستر ذخیره کن
incrementCounter
در قطعه کد بالا را اجرا کنند ، یکی از راه های محتمل برای اجرای دو نخ به شرح زیر است:یکی از نخ ها را T1
و دیگری را T2
می نامیم. فرض کنید مقدار شمارنده برابر با ۷ باشد.- اجرای
T1
در حال حاضر رویCPU
برنامه ریزی شده و وارد تابع می شود. مرحله A را انجام می دهد یعنی مقدار متغیر را از رجیستر می خواند که ۷ است. - سیستم عامل تصمیم می گیرد پروسه اجرایی را تغییر دهد و
T2
را وارد مرحله اجرایی کرده وT1
را از اجرا خارج کند. T2
وارد فاز اجرا می شود و از شانس خوبش قبل از تحویل فاز اجرایی بهT1
تمام سه مرحلهB
،A
وC
را انجام می دهد. مقدار ۷ را می خواند ، یک عدد به آن اضافه می کند و عدد ۸ را ذخیره می کند.T1
دوباره برمی گردد و از آنجا که وضعیت آن توسط سیستم عامل ذخیره شده است ، هنوز هم مقدار قدیمی متغیرcounter
یعنی ۷ را استفاده می کند این مقداری است که قبل از تغییر پروسه خوانده شده بود. نخT1
نمی داند که پشت سرش مقدار متغیر به روز شده است. متأسفانه فکر می کند که مقدار هنوز ۷ است ، یک عدد به آن اضافه می کند و حاصلجمع حاصل یعنی ۸ را بجای مقدارcounter
(که از قبل ۸ بود) می نویسد. اگر نخ ها به صورت سریالی پشت سر هم اجرا می شدند، حالا مقدار نهایی ۹ بود.
کلاس Thread unsafe
یک دقیقه وقت بگذارید و برنامه زیر را بررسی کنید. این برنامه یک شمارنده را به تعداد دفعات یکسان افزایش و کاهش می دهد. مقدار نهایی شمارنده باید صفر باشد ، با این حال ، اگر برنامه را به تعداد بار کافی اجرا کنید ، گاهی اوقات نتیجه مورد انتظار صفر را دریافت می کنید و در برخی دیگر از مواقع ، مقدار غیر صفر را دریافت می کنید. در این برنامه نخ ها را به مدتی تصادفی sleep
می کنیم یا می خوابانیم ، تا امکانی به نخ ها بدهیم که به ترتیبی غیر قطعی اجرا شوند.import java.util.Random;
class DemoThreadUnsafe {
// We'll use this to randomly sleep our threads
static Random random = new Random(System.currentTimeMillis());
public static void main(String args[]) throws InterruptedException {
// create object of unsafe counter
ThreadUnsafeCounter badCounter = new ThreadUnsafeCounter();
// setup thread1 to increment the badCounter 200 times
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
badCounter.increment();
DemoThreadUnsafe.sleepRandomlyForLessThan10Secs();
}
}
});
// setup thread2 to decrement the badCounter 200 times
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
badCounter.decrement();
DemoThreadUnsafe.sleepRandomlyForLessThan10Secs();
}
}
});
// run both threads
thread1.start();
thread2.start();
// wait for t1 and t2 to complete.
thread1.join();
thread2.join();
// print final value of counter
badCounter.printFinalCounterValue();
}
public static void sleepRandomlyForLessThan10Secs() {
try {
Thread.sleep(random.nextInt(10));
} catch (InterruptedException ie) {
}
}
}
class ThreadUnsafeCounter {
int count = 0;
public void increment() {
count++;
}
public void decrement() {
count--;
}
void printFinalCounterValue() {
System.out.println("counter is: " + count);
}
}