جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی

۲ مطلب با کلمه‌ی کلیدی «concurrency» ثبت شده است

در قسمت قبل با مفاهیم low level concurrency API صحبت کردیم و در جاوا 1.5 به بالاتر API هایی با کارایی بالاتری میتوانیم داشته باشیم و درگیر مسایل low level کمتر خواهیم شد


High Level Concurrency API : از نسخه 1.5 به جاوا اضافه شده اند و در پکیج java .util.concurrent قرار دارند و روی پردازش موازی روی پردازنده های چند هسته ای بهینه شده اند و کار با آنها و مدیریت آنها ساده تر است و برای کاربرد های متفاوت امکانات متفاوت و متنوعی را ارائه میدهند

مانند :

ArrayBlockingQueue
ConcurrentHashMap
CopyOnWriteArralyList


مفهوم Thread Safe : بعضی از کلاس ها این قابلیت را دارند که بصورت ایمن و Shared Resource همزمان در چند thread بدون درگیر شدن به استفاده از قفل synchronized از آنها استفاده کنیم


ابجکت های Immutable همیشه Thread Safe هستند چون وضعیت انها قابل تغییر نیست


اینترفیس BlockingQueue : کلاس هایی که این اینترفیس را پیاده سازی بکنند thread safe هستند . این اینترفیس متد put برای اضافه کردن و متد take برای حذف از صف مورد استفاده قرار میگیرند که در صورت لزوم هنگام نوشتن یا خواندن از صف ترد در حال اجرا موقتا متوقف میشود و مفهوم مسئله producer consumer را پیاده سازی میکند تا تداخلی ایجاد نشود 


ArrayBlockingQueue : کلاسی است که اینترفیس BlockingQueue را پیاده سازی کرده است و یک آرایه با طول ثابت را ارائه میدهد

LinkedBlockingQueue : این کلاس هم اینترفیس BlockingQueue را پیاده سازی کرده است و یک لیست پیوندی را ارئه میدهد


ابجکت های synchronizer : برای ایجاد هماهنگی بین چند ترد مورد استفاده قرار میگیرند که یک وضعیت درونی برای مدیریت ترد ها و هماهنگی آنها دارد 

مانند کلاس های :

Semaphore
CountDownLatch
Exchanger
CynclicBarrier

کلاس Semaphore : در این کلاس دسترسی به Shared Resource ها را توسط یک عدد مدیریت میکنند به این صورت که تعداد تردهای همزمان را تعیین میکنیم و دارای دو متد acquire و release است که هر تردی اگر بخواهد از Shared Resource استفاده کند باید ابتدا متد acquire را صدا بزند و اگر عدد داخلی سمافور صفر باشد ترد درخواست کننده را بلاک میکند چون تعداد ترد های همزمان استفاده کننده از حداکثر تعیین شده بیشتر است و همچنین هر ترد بعد از اتمام کار باید متد release را صدا بزند تا به عدد داخلی سمافور یکی اضافه شود و Shared Resource قابل استفاده برای ترد دیگری باشد 


کلاس CountDownLatch : یک هماهنگ کننده synchronized است که به چند ترد اجازه می دهد تا پایان شمارش معکوس یک عدد صبر کنند و بعد اجرا بشوند

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

متد await برای صبر کردن تا پایان شمارش معکوس استفاده میشود یعنی در تردی این متد فراخوانی شود اجرا ترد متوقف میشود تا شمارش معکوس توسط فراخوانی متد countDown در ترد های دیگر به صفر برسد 

متد countDown برای شمارش معکوس به اندازه یک واحد استفاده میشود 


کلاس Exchanger : یک synchronizer دیگر است که برای هماهنگی و ارتباط تنها بین دو ترد مورد استفاده قرار میگیرد بدین صورت که هر تردی که متد exchange را صدا بزند میتواند یک ابجکت به آن پاس دهد و خود ترد متوقف میشود ، آن ابجکت میتواند به ترد مقابل برسد و با فراخوانی مجدد exchange در ترد مقابل ترد قبلی از حالت توقف خارج خواهد شد و هر دو ترد به اجرا کد میپردازند


کلاس CyclicBarrier : یک synchronizer جدید است که برای رسیدن چند ترد به یک نقطه مشترک تعیین شده صبر میکنند تا ترد های دیگر هم به آن نقطه مشترک برسند مثال :

//a barrier for 3 thread
CyclicBarrier barrier = new CyclicBarrier(3);
//when thread 1 called await() method , it will be wait until two other treads called await method too
//thread 1 called and get a wait state
barrier.await();
//thread 2 called await method and get wait state
barrier.await();
//when in thread 3 called await method all 3 threads take out their wait state and continue to running
barrier.await();


کلاس Phaser : از جاوای 1.7 اضافه شده و امکانات مشابه CountDownLatch و  CyclicBarrier را ارائه میدهد اما با انعطاف بالاتر 


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

بیشتر عملیات اتمیک شامل کار روی اعداد و آرایه ها است مانند افزایش یک مقدار عددی یا خواندن و تغییر یک متغییر عددی 

برخی کلاس های Atomic موجود در جاوا :

AtomicInteger
AtomicIntegerArray
AmtomicLong
AtomicLongArray
AtomicBoolean
AtomicReference
AtomicReferenceArray

متغییر های Atomic بصورت Thread Safe هستند و در چند ترد میتوانند آنها را به اشتراک گذاشت و نیازی به داشتن Lock ندارند (Lock Free)


پکیج java.util.concurrent.locks از جاوا 1.5 اضافه شده ، در این پکیج شامل اینترفیس و کلاس هایی برای ایجاد قفل در برنامه های multithread مورد استفاده قرار میگیرد مانند اینترفیس های :

Lock
ReadWriteLock
Condition


اینترفیس Lock : با کمک این اینترفیس دسترسی چند ترد به critical section  را محدود میکنیم و در هر لحظه تنها یک ترد به بخش critical section میتواند وارد شود مشابه کاری که Lock انجام میدهد با بلوک های synchronized قبلا دیده ایم ولی فرق Lock با synchronized در چی است ؟

در synchronized قفل بصورت ضمنی است و در کد مشخص میشود که قفل به چه صورت اعمال شود

ولی در Lock بصورت صریح مشخص میکنیم که در حال حاضر قفل کجا اعمال شود و کجا آزاد شود


اینترفیس Lock سه متد مهم دارد :

متد lock برای اعمال قفل و block شدن و متد unlock برای آزاد سازی قفل استفاده میشوند و آنرا ما بصورت صریح مشخص میکنیم

متد tryLock همانند متد lock برای گرفتن قفل استفاده میشود با این تفاوت که این متد سعی میکنه قفل را بگیرد اما اگر قبلا قفل گرفته شده باشد و آزاد نباشد متد tryLock متوقف نمی شود و اصطلاحا block نمیشود و اگر نتواند قفل را بگیرد مقدار false را بر میگرداند 

کاربردش در مواقعی است که در طول برنامه همیشه نیاز نداریم که قفل روی shared resource اعمال شود


ReentrantLock : یکی از کلاس هایی که اینترفیس Lock را پیاده سازی  کرده است کلاس ReentrantLock است 

Lock lock = new ReentrantLock();
lock.lock();
try{
... // critical section
}finally{
lock.unlock();
}

در کد بالا بخش Critical section حداکثر توسط یک ترد اجرا میشود و ابجکت Lock بین چند ترد بصورت مشترک استفاده میشود 


اینترفیس ReadWriteLock : حالتی در برنامه را در نظر بگیرید که دو نوع ترد داریم یکی Shared Resource را تنها میخوانند و سری دیگر تنها مسئول تغییر آن هستند و اگر این شرایط را با بلاک های synchronized پیاده سازی کنیم حتی اگر فقط شرایط خواندن را داشته باشیم یک ترد خواننده باید منتظر باشد تا ترد قبلی کار خواندنش به پایان برسد و این کارایی کد را به شدت پایین می آورد 

در این شرایط باید از قفل های نوع  ReadWriteLock استفاده کنیم که دو قفل مجزا در نظر میگیرد و اجازه می دهد که اگر چند ترد به طور همزمان بخواهند از آن ابجکت بخوانند مشکلی وجود نداشته باشد و شامل دو متد است :

متد readLock برای گرفتن قفل خوندن و متد writeLock برای گرفتن قفل نوشتن 


ReentrantReadWriteLock : کلاسی است که اینترفیس ReadWriteLock را پیاده سازی کرده است و مثال زیر مشخص میکند که به چه صورت میتوان از آن استفاده کرد : 

List<Integer> list = new LinkedList<>();
ReadWriteLock lock = new ReentrantReadWriteLock();

class Reader extends Thread{
   public void run(){
     lock.readLock().lock();
     System.out.println(list.get(0));
     lock.readLock().unlock();
    }
}

class Writer extends Thread{
   public void run(){
     lock.writeLock().lock();
     list.add(xxx);
     lock.writeLock().unlock();
    }
}


در کد بالا اگر چندین ترد Reader وارد متد run شوند و از لیست بخواهند بخوانند مشکلی وجود ندارد ولی اگر تردی در کلاس Writer قفل write را بگیرد دیگر هیچ تردی از نوع reader نمیتواند از list بخواند و همچنین هیچ تردی از نوع writer هم نمیتواند تغییری در list اعمال کند و ترد ها باید منتظر باشند تا تردی که قفل write را گرفته است آنرا آزاد کند و اینطوری شرایط ایمنی را ایجاد خواهد کرد 


مبحث Executor ها : 

در برنامه های بزرگتر اجرا نیاز به اجرای فعالیت های مشخصی داریم که  به آن task میگوییم

در بخش دیگری از برنامه نحوه ایجاد ترد زمانبندی اجرای ترد و کلا مدیریت ترد را مشخص میکنیم 

در برنامه های بزرگ برنامه نویسی که task ها را مینویسد معمولا مدیریت ترد ها که پیچیدگی های خود را دارد را مشخص نمیکند 

فرآیند ایجاد مدیریت و اتمام یک ترد کاری پر هزینه است و سربار خود را در برنامه دارد و نیاز دارد که الگوریتم های بهتری برای بالا بردن کارایی داشته باشیم برای همین در جاوا ابجکت هایی داریم که مدیریت ترد ها را آسان تر میکنند و به آنها Executor میگویند


اینترفیس Executor : در پکیج java.util.concurrent این اینترفیس قرار گرفته و درون این اینترفیس یک متد با نام execute دارد که آرگومانی از نوع Runnable میگیرد و آنرا بدین صورت اجرا میکند :

شاید در همین ترد جاری آنرا اجرا میکند

شاید ترد جدیدی به آن اختصاص دهد 

شاید از تردی که قبلا برای همین منظور ایجاد کرده بوده مجددا استفاده کند 


Thread Pool : اغلب ما ابجکتی از نوع Executor برای مدیریت اجرای ترد ها داریم که این ابجکت ها درون خود یک Thread Pool دارند که میتوانند n تسک را توسط x ترد انجام دهند که ممکن است تعداد task از تعداد ترد ها بیشتر یا کمتر باشد

thread pool کاری که میکند از ایجاد مکرر ترد ها جلوگیری میکند و آنها را در یک فضایی آماده بکار نگه میدارد و چنانچه نیاز به اجرای task جدیدی داشتیم از همان ترد های ایجاد شده استفاده میکند و این الگو کارایی برنامه را به شدت افزایش میدهد 


کلاس Executors :  این یک کلاس کمکی برای ایجاد کلاس هایی از نوع Executor است که دارای متد های زیر است :

متد newSingleThreadExecutor : برای ایجاد thread pool با یک ترد 

متد newFixedThreadPool :  یک thread pool با تعداد مشخصی از ترد ها را ایجاد میکند و مشکلی که دارد اگر در لحظه نیاز به تعداد ترد بیشتری داشته باشیم باید درخواست های اجرا های جدید منتظر بمانند تا کار تردی خاتمه یابد

متد newCachedThreadPool :  تعداد ترد ها را از قبل تعیین نمیکنیم و برای هر اجرای جدید یک ترد جدید ایجاد میکند و انرا در pool نگه میدارد اما ممکن است با تعداد زیادی ترد که cache شده اند روبرو شویم 


اینترفیس Callable : این اینترفیس میتواند به جای Runnable استفاده شود چون Runnable دارای مشکلاتی است : 

متد run مقدار برگشتی ندارد 

معمولا جواب عملیات متد run را باید در یک shared resource بنویسیم و ترد دیگری که به آن خروجی نیاز دارد باید منتظر باشد تا خروجی آماده استفاده شود 

از طرفی shared Resource هم باید نحوه دسترسی آن مدیریت شود 

و همینطور اگر داخل متد run حین اجرای عملیات خطایی پرتاب شود تشخیص و مدیریت اجرا های وابسته مشکل تر میشود

و این موارد باعث میشه نیاز جدیدی ایجاد شود که توسط کلاس Callable آنرا پوشش دهیم

در این اینترفیس متد call را همانند run داریم و خروجی دلخواه از نوع generic خواهیم داشت و مدیریت خطا را هم خواهیم داشت چون متد call ، throws Exception دارد 


اینترفیس Future :  با این اینترفیس خروجی یک عملیات را نشان میدهیم و احتمالا در ترد دیگری میخواهیم از آن استفاده کنیم که دارای متد های زیر میباشد :

isCancelled : آیا عملیات کنسل شده ؟

isDone : آیا عملیات خاتمه پیدا کرده ؟

get : برای انتظار برای خاتمه پذیرفتن عملیات و دریافت خروجی 

cancel : برای قطع اجرای عملیات استفاده میشود


نکته که بسیاری از پیاد سازی های Executor متدی بنام submit دارند در این کلاس های از اینترفیسی بنام ExecutorService ارث بری مکنند که این متد submit درون ان تعریف شده است 

interface ExecutorService extends Executor {
   <T> Future<T> submit (Callable<T> task) ;
   ...
}

همانطور که میبینم متد submit یک آرگومان از نوع Callable میگیرد و ابجکتی از نوع Future بر میگرداند 


اگر دقت کنیم اینترفیس Executor در متد execute آرگومانی از نوع Runnable میگیرد و مقدار برگشتی void است 

اینترفیس ExecutorService آرگومانی از نوع  Callable میگیرد و مقدار برگشتی Future است 

اینترفیسی هم داریم بنام ScheduledExecutorService که همانند ExecutorService است با این تفاوت که اجرای عملیات را میتواند با تاخیر انجام دهد 


مفهوم ThreadLocal : 

فضایی است برای اجرای ایزوله کد در محل همان ترد اجرا کننده و ابجکت هایی که از این کلاس ایجاد شوند فقط داخل همان تردی که ایجاد کننده ابجکت بود قابل اجرا خواهند بود یعنی اگر دو ترد مجزا ابجکت هایی از نوع ThreadLocal داشته باشند نمیتوانند متغییر یکدیگر را تغییر بدهند و یا دسترسی پیدا کنند 

کاربردی که دارند : به اشتراگ گذاری یک بخشی از برنامه که در همان ترد قرار است اجرا شوند بدون اینکه داده ها توسط ترد های دیگری قابل دسترس باشد و دیگر نیازی به کنترل دسترسی همزمان و قفل برای دسترسی داده ها نخواهیم داشت 



deadlock : شرایطی است که چند ترد مختلف برای همیشه در حالت توقف میمانند چون منتظر هستند دیگری کارش خاتمه پیدا کند ولی هیچ وقت خاتمه پیدا نخواهند کرد چون برای خاتمه به اتمام ترد دیگری نیاز دارند این موقعی در برنامه رخ میدهد که چند بخش همروند داشته باشیم که هر کدام منتظر خاتمه پیدا کردن اجرای بخش دیگری هستن نمونه این مشکل را در چهار راه های بدون چراغ راهنمایی میتوانیم ببینیم 


startvation : حالتی است که برخی ترد ها همیشه منتظر باشند و هیچ وقت نوبت به اجرای انها نرسد و این موقعی رخ میدهد که منابع سیستم جوابگوی مقدار درخواست شده نباشد ولی این مورد خیلی کمتر از deadlock در برنامه ها قابل مشاهده است 


پیدا کردن و رفع مشکلات starvation  و deadlock در برنامه های بزرگ بسیار مشکل است برای همین نیاز به تجربه بالایی در رفع مشکلاتی این چنینی نیاز دارد و باید الگوریتم های مناسبی در بحث همروندی داشته باشیم تا کمتر به مشکلاتی این چنینی بر بخوریم و استفاده نا بجا از synchronized  و قفل ها نکنیم 


مفهوم Monitor : ابجکتی است که برای عدم دسترسی همزمان چند ترد به یک critical section استفاده میشود (همان ابجکتی که در ارگومان synchronized هم استفاده میشود)


مفهوم livelock : بسیار شبه شرایط deadlock است با این تفاوت که تردی همیشه سعی میکند عملیات را اجرا کند ولی همیشه fail میشود و این در چند شرایط رخ میدهد :

- خروجی دو عملیات به هم وابسته باشند و دایما نسبت به هم تغییر میکنند و به یک وضعیت نهایی نمیرسند که ترد مقابل از آن استفاده کند ترد های مختلف یکسری عملیات زنجیر وار را اجرا میکنند و به وضعیت پایدار و نهایی نمیرسند باید دقت کنید که ترد ها به وضعیت deadlock و قفل شده نرسیده اند و در حال اجرا هستند ولی به وضعیتی که ترد مقابل میخواهد نمیرسد

- همه ترد های دیگر مربوطه دستور wait را صادر کرده اند و نتیجه نهایی مورد استفاده ترد مورد نظر حاصل نمیشود

- و یا حالتی که در ترد به loop بی نهایت بر خورده باشیم و باز ترد در حال اجراست ولی به جواب نهایی نمیرسد


اینترفیس Condition : با استفاده از اینترفیس Condition یک ترد این اجازه را دارد هنگامی که در حال اجرای عملیاتی در critical section است برای رسیدن به یک شرایطی صبر کند تا آن شرایط احراز شود و سپس ادامه عملیات را اجرا کند این در زمانی نیاز است که یک ترد دسترسی به critical section را پیدا میکند اما شرایط اجرای آن را ندارد و برای اجرا باید یکسری شرایط اولیه فراهم شود به عنوان مثلا مشکل producer / consumer را میتوانیم ببینیم : 

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestThread {

   public static void main(String[] args) throws InterruptedException {
      ItemQueue itemQueue = new ItemQueue(10);

      //Create a producer and a consumer.
      Thread producer = new Producer(itemQueue);
      Thread consumer = new Consumer(itemQueue);

      //Start both threads.
      producer.start();
      consumer.start();

      //Wait for both threads to terminate.
      producer.join();
      consumer.join();
   }

   static class ItemQueue {
      private Object[] items = null;
      private int current = 0;
      private int placeIndex = 0;
      private int removeIndex = 0;

      private final Lock lock;
      private final Condition isEmpty;
      private final Condition isFull;

      public ItemQueue(int capacity) {
         this.items = new Object[capacity];
         lock = new ReentrantLock();
         isEmpty = lock.newCondition();
         isFull = lock.newCondition();
      }

      public void add(Object item) throws InterruptedException {
         lock.lock();

         while(current >= items.length)
            isFull.await();

         items[placeIndex] = item;
         placeIndex = (placeIndex + 1) % items.length;
         ++current;

         //Notify the consumer that there is data available.
         isEmpty.signal();
         lock.unlock();
      }

      public Object remove() throws InterruptedException {
         Object item = null;

         lock.lock();

         while(current <= 0) {
            isEmpty.await();
         }
         item = items[removeIndex];
         removeIndex = (removeIndex + 1) % items.length;
         --current;

         //Notify the producer that there is space available.
         isFull.signal();
         lock.unlock();

         return item;
      }

      public boolean isEmpty() {
         return (items.length == 0);
      }
   }

   static class Producer extends Thread {
      private final ItemQueue queue;
      
      public Producer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         String[] numbers =
            {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};

         try {
            
            for(String number: numbers) {
               System.out.println("[Producer]: " + number);
            }
            queue.add(null);
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         } 
      }
   }

   static class Consumer extends Thread {
      private final ItemQueue queue;
      
      public Consumer(ItemQueue queue) {
         this.queue = queue;
      }

      @Override
      public void run() {
         
         try {
            
            do {
               Object number = queue.remove();
               System.out.println("[Consumer]: " + number);

               if(number == null) {
                  return;
               }
            } while(!queue.isEmpty());
         } catch (InterruptedException ex) {
            ex.printStackTrace();
         }
      }
   }
}



چارچوب frok-join  :

این چارچوب یک پیاده سازی از ExecutorService برای گرفتن حداکثر توان پردازشی سیستم است که در آن کل عملیات به عملیات کوچکتری بصورت بازگشتی تقسیم میشود و در ترد های جداگانه بصورت موازی اجرا میشوند

با استفاده از چارچوب fork-join میتوانیم عملیاتی پیچیده را در چند ترد بشکنیم و نتایج نهایی آنها را با هم ترکیب کنیم و اینطوری از حداکثر توان پردازنده های چند هسته امروزی استفاده کنیم و به طور خاص تر میتوان گفت که اگر عملیات کوچک است انرا انجام بده و اگر بزرگ است آنرا به چند task کوچک تبدیل و بعد بصورت موازی اجرا کن

Fork : این مفهوم برای شکستن یک task بزرگ به sub-task های کوچک تر است تا از concurrency بهترین پرفرمانس را بگیریم

Join : این مفهوم برای ترکیب نتایج بدست امده از sub-task های کوچک است و تا موقعی که کلیه عملیات در sub-task ها خاتمه نیافته باشد در حالت انتظار نگه داشته می شود تا اخرین sub-task هم عملیاتش خاتمه یابد 


مثل تمامی پیاده ساز های ExecutorService که از یک Thread Pool استفاده میکند و مزیتی که دارد از الگوریتم work-stealing استفاده میکند و تردهای بیکار کار را از تردهای مشغول می دزدند و اجرا میکنند

برای اجرا کدمان در قالب fork-join باید بصورت بازگشتی آنرا بنویسیم و باید کد ما در زیر کلاسی از RecursiveAction یا RecursiveTask نوشته شوند 

کلاس ForkJoinPool : از جاوای 1.7 اضافه شده و یک Thread Pool خاص برای انجام عملیات fork و join می باشد که کار را ساده تر میکند

کلاس RecursiveAction : برای پیاده سازی یک task بدون خروجی میباشد و یک متد compute دارد

class Writer extends RecursiveAction {
   @Override
   protected void compute() { }
}

کلاس RecursiveTask : برای پیاده سازی یک task همراه با خروجی می باشد و دارای متد compute میباشد 

class Sum extends RecursiveTask<Long> {
   @Override
   protected Long compute() { return null; }
}

مثالی که از چارچوب fork-join استفاده شده است :

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class TestThread {

   public static void main(final String[] arguments) throws InterruptedException, 
      ExecutionException {
      
      int nThreads = Runtime.getRuntime().availableProcessors();
      System.out.println(nThreads);
      
      int[] numbers = new int[1000]; 

      for(int i = 0; i < numbers.length; i++) {
         numbers[i] = i;
      }

      ForkJoinPool forkJoinPool = new ForkJoinPool(nThreads);
      Long result = forkJoinPool.invoke(new Sum(numbers,0,numbers.length));
      System.out.println(result);
   }  

   static class Sum extends RecursiveTask<Long> {
      int low;
      int high;
      int[] array;

      Sum(int[] array, int low, int high) {
         this.array = array;
         this.low   = low;
         this.high  = high;
      }

      protected Long compute() {
         
         if(high - low <= 10) {
            long sum = 0;
            
            for(int i = low; i < high; ++i) 
               sum += array[i];
               return sum;
         } else {	    	
            int mid = low + (high - low) / 2;
            Sum left  = new Sum(array, low, mid);
            Sum right = new Sum(array, mid, high);
            left.fork();
            long rightResult = right.compute();
            long leftResult  = left.join();
            return leftResult + rightResult;
         }
      }
   }
}



مفهوم Multithread : در یک پروسس بتوان بخش های مختلف برنامه را بصورت همزمان اجرا کرد

Thread of execution : هر جریان اجرایی را یک thread of execution میگویند 

Parallel processing : در پردازنده های بالای یک هسته میتوانیم چند بخش از برنامه را واقعا و در یک لحظه اتمیک بصورت موازی اجرا کنیم هر اجرای موازی یک اجرا Concurrent هم محسوب میشود ولی نه برعکس

Concurrency : به اجرای همزمان چند بخش همروندی یا concurrency گفته میشود که لزوما این پردازش ها بصورت Parallel اجرا نمیشوند مثلا میتواند در یک هسته از پردازنده اتفاق بیوفتد به این شکل که جریان اجرایی تا یک مرحله انجام میشود و بعد جریان اجرایی دیگری انجام میشود و در هر لحظه اتمیک میتوان دید که فقط یک Thread در حال اجرا هستند لذا در پردازنده هایی با تعداد هسته کم اگر از Multithread استفاده کنیم ممکن است با کاهش Performance روبرو بشویم ولی داشتن خاصیت هم روندی در برنامه مفید است

برای ایجاد ترد در جاوا دو راه وجود دارد یکی ارث بری از کلاس Thread و یا پیاده سازی کلاس Runnable حال کدامیک بهتر است ؟

وقتی کلاس ما از Thread ارث بری میکند موقع اجرا تنها با فراخوانی متد start کد اجرا میشود و پیاده سازی راحتی دارد ولی مشکلی که وجود دارد چون در جاوا multi inheritance نداریم امکان اینکه کلاس ما از کلاس دیگری ارث بری بکند وجود نخواهد داشت 

وقتی کلاس ما کلاس Runnable را پیاده سازی میکند بعدا باید آنرا توسط یک ابجکت جدید Thread در یک ترد جدید اجرا کنیم که خب پیاده سازی را کمی بیشتر میکند ولی چون Runnable یک اینترفیس است کلاس ما آزاد است که از هر کلاس دیگری ارث بری داشته باشد

وقتی thread ای ایجاد شد میتوانیم از طریق متد start آنرا اجرا کنیم سوال اینجاست که فراخوانی متد run هم برنامه را اجرا میکند آیا با فراخوانی متد run واقعا یک ترد جدید ایجاد میشود ؟
با فراخوانی متد start واقعا از سیستم عامل یک ترد جدید گرفته میشود و کد در آن ترد اجرا میشود ولی با فراخوانی run ترد جدیدی ایجاد نمیشود و فقط بصورت method invocation متد run را فراخوانی کرده ایم و برنامه در تردی که متد run را فراخوانی کرده است اجرا میشود 

گرفتن ترد جاری : با استفاده از ()Thread thread = Thread.currentThread میتوان به ترد جاری دست پیدا کرد 

join کردن : گاهی لازم است کار یک ترد تمام شود تا اجرای بخش دیگری ادامه پیدا کند با متد join میتوانیم ترد را به حالت موقتی متوقف کنیم تا کار ترد دیگری انجام شود و بعد ادامه اجرای برنامه را برویم 
متد های join و sleep میتوانند خطای InterruptedException تولید کنند

priority : ما میتوانیم اولیت اجرای یک ترد را با setPriority مشخص کنیم و عددی بین 1 تا 10 ارسال کنیم هر چه عدد به 10 نزدیک تر باشد الویت اجرا بالاتری را دارد ولی تضمینی وجود ندارد و این بر عهده سیستم عامل است که طبق الویتی که ما مشخص کردیم آن ترد را با الویت بیشتری اجرا کند یا نه . در حالت عادی همه ترد ها الویت 5 دارند

Daemon Thread : ترد شبح نوع خاصی از ترد ها هستند که در پس زمینه اجرا میشوند و معمولا خودشان مستقلا کار خاصی انجام نمیدهند و کارشان انجام خدماتی به ترد های دیگر است مثلا Garbage Collector خود یک Daemon Thread است ما متوانیم با متد setDaemon مشخص کنیم که این ترد از نوع شبح باشد . چنانچه اجرای تمامی ترد ها به اتمام رسیده باشد JVM اجرای Daemon Thread ها را هم خودکار خاتمه میدهد

همه ترد ها از حافظه Heap بطور مشترک استفاده میکنند ولی هر کدام حافظه Stack اختصاصی خودشان را دارند برای همین موقع Debug کردن باید به این نکته دقت داشت و همین متفاوت بودن Stack ها دیباگ کردن را مشکل تر میکند

Shared Resource : یک موجودیتی (متغییر ، فایل ، ابجکت و ...) که همزمان در چند ترد مورد استفاده قرار میگیرد

Race Condition : شرایطی است که چند ترد میخواهند به یک Shared Resource دسترسی پیدا کنند و حداقل یکی از ترد ها سعی کند تغییری در منبع ایجاد کند 
 
critical section : اینکه چند ترد میتوانند از یک شی مشترک استفاده کنند مشکلاتی را هم ایجاد میکند:
به طور مثال دو ترد در حال تغییر دادن یا خواندن یک شی باشند 
یا یک ترد شروع کند درون یک فایل محتویاتی را بنویسد و ترد دیگر آن فایل را ببندد 
به این حالت ها که Race Condition بوجود می آید critical section میگوییم حالتی است که نمیخواهیم توسط چند ترد همزمان اجرا شوند

Mutual Exclusion یا Mutex : انحصار متقابل یعنی چند ترد نباید بخش critical section خود را اجرا بکنند و با ورود یک ترد به بخش بحرانی باید از ورود بقیه ترد ها جلوگیری شود 

Synchronized : جاوا برای پیاده سازی Mutex از کلمه کلیدی Synchronized استفاده میکند و هر نخ هنگام ورود به یک Critical Section یک قفل (Lock) را در اختیار میگیرد و هنگام خروج آن قفل آزاد میشود و اینطوری Shared Resource ما محافظت میشود 

برای روشن شدن مفهوم Synchronized به مثال زیر دقت کنید 

public class BnakAccount {
private int balance;
public synchronized void withdraw (int amount){
     balance -= amount;
}
public synchronized void deposit (int amount){
     balance += amount;
}
}
در این کلاس موجودی یک حساب بانکی نگهداری میشود فرض کنید این کلاس توسط ترد های گوناگونی مورد استفاده قرار میگیرد و همینطور نباید موقعی که وجهی به حساب اضافه میشود وجهی از آن کم شود (deposit و withdraw) و باید در هر لحظه یک عملیات توسط یک ترد انجام شود ولی اگر از کلاس BankAccount یک ابجکت دیگری ساخته بودیم شرایط در ان ابجکت اعمال میشود و synchronized در سطح instance اعمال میشود 
وقتی ما متدی بصورت synchronized داریم قفل روی this اعمال میشود 
وقتی در یک کلاس بیش از یک متد synchronized داشته باشیم و توسط تردی یکی از آنها در حال استفاده باشد دیگر متد های synchronized توسط ترد های دیگر قابل استفاده نخواهند بود و باید صبر کنند تا قفل آزاد شود 

هر شی ای در جاوا میتواند به عنوان Lock استفاده شود 

synchronized block : در جاوا میتوان توسط synchronized block قفل را روی هر ابجکتی ایجاد کرد و فقط محدود به this نیست 
List<String> names;
synchronized (names){
   names.add( "sara" );
}

ّبلاک بالا به این معنی است که وقتی تردی وارد synchronized block شد ترد دیگری نمیتواند ابجکت names را در اختیار داشته باشد 
synchronized void m1(){
    run();
}
void m2(){
synchronized (this) {
run();
}
}


دو متد بالا کاملا یکسان هستند و وقتی از عبارت synchronized روی متد استفاده مکنیم مانند این است که داخل متد از synchronized block روی ابجکت this استفاده کرده باشیم 

ما میتوانیم متد های static را هم synchronized کنیم و وقتی تردی وارد یک متد synchronized شود قفل کل کلاس (و نه instance )را در اختیار میگیرد و شرایط سختگیرانه تر است

تعامل بین چند ترد :
گاهی لازم است دو ترد باهم تعامل داشته باشند مثلا یک ترد در میانه اجرا باید صبر کند تا ترد دیگری به آن خبر بدهد که میتواند ادامه کار را انجام دهد . متد های notify و wait برای تعامل بین ترد ها استفاده میشود این متد ها در کلاس Object هستند و بصورت final و native هستند 
وقتی یک ترد متد wait را روی یک ابجکت فراخوانی میکند اجرای ان ترد متوقف میشود و این توقف تا زمانی که ترد دیگر متد notify آن ابجکت را فراخوانی کند طول میکشد 
متد wait و notify تنها در صورتی قابل استفاده هستند که آن ابجکت در داخل synchronized block قرار داشته باشد 
پس یک ترد برای فراخوانی wait  و notify روی یک شی باید قفل آن شی را در اختیار داشته باشد وگرنه خطای IllegalMonitorStateException پرتاب میشود البته با فراخوانی wait روی ابجکت بلافاصله قفل آزاد میشود تا ترد های دیگر بتوانند وارد synchronized block شوند و notify را صدا بزنند تا از حالت waiting خارج شود متد wait کمی خاص تر است 

public class Name {
sycnchronized void m1(){
    wait(); }
}

synchronized (name){
   name.notify(); }

ما روی هر ابجکت تعدادی ترد میتوانیم داشته باشیم که wait کرده باشند ، هر ابجکتی فهرستی از ترد های منتظر دارد و با هر فراخوانی notify روی ابجکت یکی از آن ترد ها از حالت wait خارج میشود و اجرایشان را ادامه میدهند 
بجای فراخوانی notify میتوانیم notifyAll را هم میتوانیم فراخوانی کنیم و همه ترد ها را از حالت wait خارج کنیم 
متد wait میتواند برای حالت انتظارش زمان تعیین بکند و بعد از اتمام زمان اتوماتیک از حالت انتظار خارج میشود 

متد interrupt : 
گاهی ممکن است اجرای یک ترد به خاطر یکی از حالت های wait sleep join به حالت انتظار رفته باشد اگر در این حالت متد interrupt را فراخوانی کنیم ترد از حالت انتظار خارج میشود و احتمالا یک خطای InterruptException رخ خواهد داد 


Running State در ترد : به زمانی که ترد حالت کاری دارد و میتواند کد را اجرا کند Running State میگویند
Alive State : از زمان بوجود امدن یک ابجکت Thread تا لحظه اتمام فرایند کاری و قبل از مرگ آن ترد Alive State میگویند
با متد getState میتواند State ترد را دریافت کرد