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

java programming language

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

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

در قسمت قبل با مفاهیم 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;
         }
      }
   }
}



نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی