سلام،
توی این تاپیک کمتر دیدم دوستان از مباحث multi threading و ... مطلب قرار بدن.
یکی از موضوعاتی که به تازگی اهمیت پیدا کرده و به شدت تو افزایش سرعت اجرای تسک ها تاثیر گذاره، تقسیم کردن تسک های بزرگ به چندین تسک کوچیک و اجرای همزمان اون هاست.
پیکیج java.util.concurrent یک API کاملیه که مخصوصا برای مدیریت بهتر concurrency ، بهره گیری کامل از توان سیستم برای انجام تسک ها و افزایش نظم و سرعت در اجرای برنامه های multi thread فراهم شده.
واقعا حیفه که یه نگاهی به این پکیج و کلاس هاش انداخته نشه...
برای مثال محاسبه فاکتوریل یک عدد نسبتا بزرگ میتونه کاملا اهمیت تجزیه تسک ها رو نشون بده هر چند مثال کاربردی نیست اما میتونید با دیدن این مثال برای موارد دیگه هم ایده بگیرید.
فرض کنید میخوایم فاکتوریل عدد 100 رو حساب کنیم.
یعنی :
1 * 2 * 3 * ... * 100
اگر بخوایم محاسبه این فاکتوریل رو به محاسبات کوچیک تر تقسیم کنیم :
1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21 * 22 * 23 * 24 * 25
26 * 27 * 28 * 29 * 30 * 31 * 32 * 33 * 34 * 35 * 36 * 37 * 38 * 39 * 40 * 41 * 42 * 43 * 44 * 45 * 46 * 47 * 48 * 49 * 50
51 * 52 * 53 * 54 * 55 * 56 * 57 * 58 * 59 * 60 * 61 * 62 * 63 * 64 * 65 * 66 * 67 * 68 * 69 * 70 * 71 * 72 * 73 * 74 * 75
76 * 77 * 78 * 79 * 80 * 81 * 82 * 83 * 84 * 85 * 86 * 87 * 88 * 89 * 90 * 91 * 92 * 93 * 94 * 95 * 96 * 97 * 98 * 99 * 100
حالا با محاسبه همه دنباله ها به صورت موازی داریم :
15511210043330985984000000
1960781468160819415703172080467968000000
815712000579973729423615859451974909952000000
3761767332187389431968739190317715670695936000000
در آخر با ضرب همه نتیجه ها فاکتوریل عدد 100 رو حساب میکنیم :
93326215443944152681699238856266700490715968264381 62146859296389521759999322991560894146397615651828 62536979208272237582511852109168640000000000000000 00000000
FactorialCalculator :
package com.sample.factorial;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ForkJoinPool;
/**
* @author avb
*/
public class FactorialCalculator {
private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
private static final int OUTER_CHUNK_SIZE = AVAILABLE_PROCESSORS * AVAILABLE_PROCESSORS * 2;
private static final int INNER_CHUNK_SIZE = AVAILABLE_PROCESSORS;
private boolean parallel;
public FactorialCalculator parallel() {
parallel = true;
return this;
}
public FactorialCalculator nonParallel() {
parallel = false;
return this;
}
public boolean isParallel() {
return parallel;
}
public BigInteger calculate(int n) {
if (n < 0) {
throw new IllegalArgumentException();
}
if (n == 0) {
return BigInteger.ONE;
}
Sequence sequence = new Sequence(1, n);
if (!parallel) {
return sequence.multiplySequence();
}
List<BigInteger> elements = parallelCalculate(splitSequence(sequence.getElemen ts(), OUTER_CHUNK_SIZE));
while (elements.size() > INNER_CHUNK_SIZE) {
elements = parallelCalculate(splitSequence(elements, INNER_CHUNK_SIZE));
}
return new Sequence(elements).multiplySequence();
}
private List<Sequence> splitSequence(List<BigInteger> elements, int chunkSize) {
int index = 0, targetIndex, elementsSize = elements.size();
List<Sequence> subSequences = new ArrayList<>();
while (index < elementsSize) {
targetIndex = Math.min(index + chunkSize, elementsSize);
subSequences.add(new Sequence(elements.subList(index, targetIndex)));
index = targetIndex;
}
return subSequences;
}
private List<BigInteger> parallelCalculate(List<Sequence> sequences) {
CountDownLatch latch = new CountDownLatch(sequences.size());
List<BigInteger> results = Collections.synchronizedList(new ArrayList<>());
ForkJoinPool pool = new ForkJoinPool(AVAILABLE_PROCESSORS);
sequences.parallelStream().forEach(sequence -> pool.submit(() -> {
results.add(sequence.multiplySequence());
latch.countDown();
}));
pool.shutdown();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return results;
}
public static class Sequence {
private final List<BigInteger> elements;
public Sequence(List<BigInteger> elements) {
this.elements = elements;
}
public Sequence(int from, int to) {
if (to < from) {
throw new IllegalArgumentException();
}
elements = new ArrayList<>();
for (; from <= to; from++) {
elements.add(BigInteger.valueOf(from));
}
}
public List<BigInteger> getElements() {
return elements;
}
public BigInteger multiplySequence() {
BigInteger result = BigInteger.ONE;
for (BigInteger element : elements) {
result = result.multiply(element);
}
return result;
}
}
}
Test :
package com.sample.factorial;
/**
* @author avb
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
final FactorialCalculator factorialCalculator = new FactorialCalculator();
for (int i = 0; i < 10; i++) {
System.out.println("test " + (i + 1) + " : ");
long l = System.currentTimeMillis();
factorialCalculator.parallel().calculate(1000000);
System.out.println("Execution time : " + (System.currentTimeMillis() - l) + " ms");
System.out.println("----------------------------");
}
}
}
برای مقایسه محاسبه فاکتوریل عدد 1000000 به صورت معمولی حدودا 5-3 دقیقه طول میکشه اما به صورت parallel حدودا 7 ثانیه روی یه پردازنده 8 هسته طول میکشه !!! به شدت که میگم برای اینه :))
دانلود src