java, جاوا, همزمانی در جاوا (MultiThreading)

synchronized در جاوا (MultiThreading in Java)

synchronized در جاوا

در این آموزش  تیم کدگیت را با ادامه thread در جاوا را همراهی کنید. در جلسه پیش در مورد نحوه ایجاد thread صحبت شد. این بار میخواهیم در مورد synchronized در جاوا صحبت کنیم پس پیش نیاز این آموزش جلسه اول thread در جاوا است.

مشکل Thread

قبل از اینکه درباره synchronized در جاوا صحبت کنیم به مشکل thread میپردازیم. وقتی ما دو یا چند Thread را همزمان اجرا می کنیم ممکن است وضعیتی پیش بیاید که چند Thread همزمان تلاش به دسترسی به یک منبع را بکنند و نتیجه پیش بینی نشده ای به وجود بیاید برای مثال وقتی چند Thread همزمان بخواهند در یک فایل بخصوصی اطلاعات بنویسند. این کار باعث میشود اطلاعات ما به صورت خراب در فایل ذخیره شود.

بگذارید یک مثال ساده بزنم و شما بیشتر با مشکل گفته شده آشنا شوید. ما یک برنامه ساده می نویسیم به این صورت که دو thread اجرا شده و هر دو یک متد را صدا بزنند. نام متد increment است و یک متغیر را با یک جمع میکند. متغیر ما یک فیلد است به نام count کد برنامه به شکل زیر است.

public class TestThreadProblem {

     int count = 0;

     public void increment() {
          count++;
     }

     public void runThreads() {

          Thread th1 = new Thread(new Runnable() {

              public void run() {
                   for (int i = 0; i < 10000; i++) {
                        increment();
                   }

              }
          });

          Thread th2 = new Thread(new Runnable() {

              public void run() {
                   for (int i = 0; i < 10000; i++) {
                        increment();
                   }
              }
          });

          th1.start();
          th2.start();

          try {
              th1.join();
              th2.join();
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }

          System.out.println(count);

     }

     /**
      * @param args
      */
     public static void main(String[] args) {
          new TestThreadProblem().runThreads();
     }

}

به صورت خلاصه کد بالا شامل

  1. RunThreads : متدی که دو thread ما را می سازد.
  2. Count : شمارنده ما
  3. Run : متد که در thread است و 10000 بار شمارنده count را با یک جمع می کند.
  4. Start: متدی که thread ما را آغاز می کند.
  5. Join: متدی که منتظر می ماند thread مورد نظر تمام شود سپس برنامه را ادامه می دهد.
  6. Increment: متدی که شمارنده ما را با یک جمع میکند.
  7. Main: نیاز به توضیح نداره!!!!!!

در نتیجه برنامه ما باید خروجی count را چاپ کند. طبق توضیحاتی داده شد هر thread  برابر با 10000 بار شمارنده را با یک جمع می کنند و ما دو thread داریم پس باید 20000 شود شمارنده ما بعد از اتمام thread. ولی خروجی برنامه به صورت زیر است:

همانطور که میبینید خروجی آن چیزی نیست که ما میخواهیم. دلیل این است که وقتی ما شمارنده با یک جمع میکنیم کامپیوتر چندین مرحله میگذرد تا این عمل انجام دهد و اگر قبل از این مراحل thread دیگری وارد شود شمارنده را مقدار قبلی میبیند و بعد جمع ما به جای این که دوبار انجام شده باشد انگار یک بار انجام شده است.  در تصویر زیر همانطور که میبینید 2 thread همزمان increment را صدا زدند وقتی اولین thread در حال تغییر count است(count هنوز تغییر نکرده) در همان موقع thread دوم وارد شده و وقتی میخواهد count را جمع کند آن  را 10 میبیند(چون هنوز تغییر نکرده توسط thread اول) به جای 11.

synchronized در جاوا

یک راه حل ساده برای مشکل گفته شده دارد آن هم استفاده از synchronized در جاوا است. Synchorized یک بلاک است که ما آن قسمت از   Threadها که اشتراک دارند درون آن قرارمی دهیم. synchronized در جاوا یک object به عنوان lock میگیرد که در آموزش های بعدی به آن پرداخته خواهد شد و در این آموزش جای آن this قرار می دهیم.در زیر کد بالا که مشکل داشت را با synchronized در جاوا نوشتیم.

public class TestSolveThreadProblem {

     int count = 0;

     public void increment() {
          synchronized (this) {
              count++;
          }

     }

     public void runThreads() {

          Thread th1 = new Thread(new Runnable() {

              public void run() {
                   for (int i = 0; i < 10000; i++) {
                        increment();
                   }

              }
          });

          Thread th2 = new Thread(new Runnable() {

              public void run() {
                   for (int i = 0; i < 10000; i++) {
                        increment();
                   }
              }
          });

          th1.start();
          th2.start();

          try {
              th1.join();
              th2.join();
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }

          System.out.println(count);

     }

     /**
      * @param args
      */
     public static void main(String[] args) {
          new TestSolveThreadProblem().runThreads();

     }

}

خروجی کد بالا طبق انتظار ما باید 20000 بدهد و همینطور هم هست.

کاری که synchronized در جاوا انجام میدهد این است که اجازه نمیدهد بیشتر از یک thread وارد بلاکش شود و این کار را با lock کردن بلاک خود انجام می دهد. وقتی thread بخواهد وارد بلاک بشود اول چک میشود که آیا lock هست بلاک یا نه. اگر lock بود منتظر میماند تا بلاک از حالت lock خارج شود.همانطور که در تصویر زیر میبینید thread دوم منتظر میماند thread اول از بلاک خارج شود.

نوشته های مشابه

5 دیدگاه در “synchronized در جاوا (MultiThreading in Java)

  1. رضا گفت:

    با سلام خیلی ممنون از مطلب خوبتون
    من یک سوال داشتم مگر جوین قرار نیست thread بعدی را منتظر بگذارد تا thread اول کارش تمام شود، پس چرا مشکلی ک فرمودین بوجود آمد، منظورم این است ک جوین رو وقتی گذاشتین threadدوم چرا منتظر نموند ترید اول کارشو تمام کنه، پس اگر قرار نبود منتظر بشه، پس اصلا گذاشتن یا نگذاشتنن جوین چ فرقی میکنه

    1. سلام. خسته نباشید.
      متد Join فرض کنید در خط 10 برنامه قرار دارد. کار این متد این است که تا وقتی Thread که متد Join آن را صدا زدیم تمام نشده خط بعدی برنامه را اجرا نکند. فرض کنیم Thread1 متد Join آن را صدا زدیم. برنامه به خط 10 میرسد همانطور که گفتیم Thread1 که تمام شد خط 11 اجرا می‌شود اما آیا وقتی در خط 10 بودیم Thread های دیگری در حال اجرا نیستند؟ در خطوط قبل برنامه ما چندین Thread اجرا کردیم و در حال اجرای همزمان هستند. منظور از انتظار در متد Join این است. حال اگر سوال یا توضیح بیشتری نیاز دارید در همین قسمت Reply دهید. تشکر از شما دوست گرامی
      سعید غریبی

  2. Ali گفت:

    عااااااااااااااالی ممنونم از اموزشتون 3>

  3. طباطبائي سيدعارف گفت:

    خیلی خوب توضیح داده بودید ممنون

  4. اسماعیل گفت:

    خیلی این موضوع ساده و کاربردی توضیح داده شده است ممنون از شما

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *