Giriş
Axınlar maraqlı bir şeydir. Əvvəlki təhlillərdə biz çox iş parçacığını həyata keçirmək üçün bəzi mövcud vasitələrə baxdıq. Gəlin görək daha nə maraqlı şeylər edə bilərik. Bu məqamda biz çox şey bilirik. Məsələn, “
Java-nı Mövzu ilə korlaya bilməzsiniz: I hissə - Mövzular ”dan biz bilirik ki, mövzu bir Mövzudur. Biz bilirik ki, ip hansısa vəzifəni yerinə yetirir. Tapşırığımızın işləyə bilməsini istəyiriksə (
run
), onda mövzunun müəyyən olmasını göstərməliyik
Runnable
.
Xatırlamaq üçün Tutorialspoint Java Online Compiler-dən istifadə edə bilərik :
public static void main(String []args){
Runnable task = () -> {
Thread thread = Thread.currentThread();
System.out.println("Hello from " + thread.getName());
};
Thread thread = new Thread(task);
thread.start();
}
Kilid kimi bir anlayışımız olduğunu da bilirik.
Bu barədə " Java-nı bir mövzu ilə korlaya bilməzsiniz: II hissə - Sinxronizasiya " məqaləsində oxuyuruq . Bir ip bir kilidi tuta bilər və sonra kilidi tutmağa çalışan başqa bir ip kilidin boşalmasını gözləmək məcburiyyətində qalacaq:
import java.util.concurrent.locks.*;
public class HelloWorld{
public static void main(String []args){
Lock lock = new ReentrantLock();
Runnable task = () -> {
lock.lock();
Thread thread = Thread.currentThread();
System.out.println("Hello from " + thread.getName());
lock.unlock();
};
Thread thread = new Thread(task);
thread.start();
}
}
Düşünürəm ki, maraqlı olan başqa nə edə biləcəyimiz barədə danışmağın vaxtıdır.
Semaforlar
Neçə ipin eyni vaxtda işləyə biləcəyini idarə etmək üçün ən sadə vasitə semafordur. Dəmir yolunda olduğu kimi. Yaşıl işıq yanır - edə bilərsiniz. Qırmızı işıq yanır - gözləyirik. Semafordan nə gözləyirik? İcazələr. İngilis dilində icazə - icazə. İcazə almaq üçün ingilis dilində əldə ediləcək icazəni əldə etməlisiniz. İcazə artıq lazım olmadıqda, biz onu verməliyik, yəni azad etməliyik və ya ondan qurtulmalıyıq ki, bu da ingilis dilində buraxılacaq. Gəlin görək necə işləyir. Sinfi idxal etməliyik
java.util.concurrent.Semaphore
. Misal:
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(0);
Runnable task = () -> {
try {
semaphore.acquire();
System.out.println("Finished");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(task).start();
Thread.sleep(5000);
semaphore.release(1);
}
Gördüyümüz kimi, ingilis sözlərini əzbərləyərək semaforun necə işlədiyini başa düşürük. Maraqlıdır ki, əsas şərt odur ki, semafor “hesabında” müsbət sayda icazə olmalıdır. Buna görə bir mənfi ilə başlaya bilərsiniz. Və 1-dən çox tələb edə (əldə edə bilərsiniz).
CountdownLatch
Növbəti mexanizmdir
CountDownLatch
. İngilis dilində Countdown geri sayımdır, Latch isə bolt və ya kiliddir. Yəni onu tərcümə etsək, bu, geri sayımı olan bir qapaqdır. Burada sinfin müvafiq idxalına ehtiyacımız var
java.util.concurrent.CountDownLatch
. Bu, hər kəsin start xəttinə toplaşdığı və hamı hazır olduqdan sonra icazənin verildiyi və hamının eyni vaxtda başladığı yarışa və ya yarışa bənzəyir. Misal:
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(3);
Runnable task = () -> {
try {
countDownLatch.countDown();
System.out.println("Countdown: " + countDownLatch.getCount());
countDownLatch.await();
System.out.println("Finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
}
ingiliscə gözləmək - gözləmək. Yəni ilk biz danışırıq
countDown
. Google Tərcüməçinin dediyi kimi, geriyə sayma “rəqəmlərin tərs ardıcıllıqla sıfıra sayılması aktıdır”, yəni məqsədi sıfıra qədər saymaq olan geri sayım hərəkətini yerinə yetirməkdir. Və sonra deyirik
await
- yəni sayğacın dəyəri sıfır olana qədər gözləyin. Maraqlıdır ki, belə bir sayğac birdəfəlik istifadə olunur. JavaDoc-da deyildiyi kimi - "Mövzular bu şəkildə dəfələrlə geri sayılmalı olduqda, bunun əvəzinə CyclicBarrier istifadə edin", yəni təkrar istifadə edilə bilən saymağa ehtiyacınız varsa, başqa bir seçimdən istifadə etməlisiniz, buna
CyclicBarrier
.
Cyclic Barrier
Adından da göründüyü kimi,
CyclicBarrier
bu, tsiklik bir maneədir. Sinfi idxal etməliyik
java.util.concurrent.CyclicBarrier
. Bir misala baxaq:
public static void main(String[] args) throws InterruptedException {
Runnable action = () -> System.out.println("На старт!");
CyclicBarrier berrier = new CyclicBarrier(3, action);
Runnable task = () -> {
try {
berrier.await();
System.out.println("Finished");
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
};
System.out.println("Limit: " + berrier.getParties());
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
}
Gördüyünüz kimi, ip icra olunur
await
, yəni gözləyir. Bu vəziyyətdə maneənin dəyəri azalır.
berrier.isBroken()
Geri sayım sıfıra çatdıqda maneə pozulmuş hesab olunur ( ). Baryeri sıfırlamaq üçün zəng etmək lazımdır
berrier.reset()
, hansı ki
CountDownLatch
.
dəyişdirici
Növbəti vasitə budur
Exchanger
. İngilis dilindən mübadilə mübadilə və ya mübadilə kimi tərcümə olunur. A
Exchanger
dəyişdiricidir, yəni mübadilə etdikləri bir şeydir. Sadə bir misala baxaq:
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
Runnable task = () -> {
try {
Thread thread = Thread.currentThread();
String withThreadName = exchanger.exchange(thread.getName());
System.out.println(thread.getName() + " обменялся с " + withThreadName);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(task).start();
new Thread(task).start();
}
Burada iki mövzu işə salırıq. Onların hər biri mübadilə metodunu yerinə yetirir və mübadilə metodunu başqa bir ipin də yerinə yetirməsini gözləyir. Beləliklə, mövzular keçən arqumentləri öz aralarında mübadilə edəcəklər. Maraqlı şey. O sizə heç nəyi xatırlatmır? Və xatırladır
SynchronousQueue
ki, 'a'nın qəlbində yerləşir
cachedThreadPool
. Aydınlıq üçün bir nümunə:
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<String> queue = new SynchronousQueue<>();
Runnable task = () -> {
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(task).start();
queue.put("Message");
}
Nümunə göstərir ki, yeni başlıq açmaqla bu mövzu gözləmə rejiminə keçəcək, çünki növbə boş olacaq. Və sonra
main
mövzu "Mesaj" mətnini növbəyə qoyacaq. Eyni zamanda, bu mətn elementini növbədən alana qədər tələb olunan vaxt dayanacaq. Bu mövzuda siz həmçinin oxuya bilərsiniz "
SynchronousQueue Vs Exchanger ".
Fazer
Və nəhayət, ən şirin şey -
Phaser
. Sinfi idxal etməliyik
java.util.concurrent.Phaser
. Sadə bir misala baxaq:
public static void main(String[] args) throws InterruptedException {
Phaser phaser = new Phaser();
phaser.register();
System.out.println("Phasecount is " + phaser.getPhase());
testPhaser(phaser);
testPhaser(phaser);
testPhaser(phaser);
Thread.sleep(3000);
phaser.arriveAndDeregister();
System.out.println("Phasecount is " + phaser.getPhase());
}
private static void testPhaser(final Phaser phaser) {
phaser.register();
new Thread(() -> {
String name = Thread.currentThread().getName();
System.out.println(name + " arrived");
phaser.arriveAndAwaitAdvance();
System.out.println(name + " after passing barrier");
}).start();
}
Nümunə göstərir ki,
Phaser
'a istifadə edərkən, qeydiyyatların sayı maneəyə gələnlərin sayı ilə üst-üstə düşəndə maneə pozulur.
Daha çox məlumatı " New Phaser synchronizerPhaser
" mərkəzindən məqalədə tapa bilərsiniz .
Nəticələr
Nümunələrdən göründüyü kimi, ipləri sinxronlaşdırmağın müxtəlif yolları var. Əvvəllər multithreading haqqında bir şey xatırlamağa çalışdım, ümid edirəm əvvəlki hissələr faydalı oldu. Onlar deyirlər ki, multithreading yolu "Java Concurrency in Practice" kitabından başlayır. 2006-cı ildə çıxmasına baxmayaraq, insanlar kitabın kifayət qədər fundamental olduğunu və hələ də bir zərbə olduğunu söyləyirlər. Məsələn, burada müzakirələri oxuya bilərsiniz: "
Java Paralelliyi Təcrübədə hələ də etibarlıdırmı? ". Müzakirədən bağlantıları oxumaq da faydalıdır.
Məsələn, " The Well-Grounded Java Developer " kitabına keçid var , orada "
Fəsil 4. Müasir paralellik " ə diqqət yetirməyə dəyər . Eyni mövzuda başqa bir bütöv icmal var: “
Java 8-in erasında praktikada Java cocurrency hələ də aktualdırmı ?”. Mövzunu həqiqətən başa düşmək üçün başqa nə oxumalı olduğuna dair məsləhətlər də var.
Bundan sonra siz " OCA OCP JavaSE 8 Proqramçı Təcrübə Testləri " kimi gözəl kitaba daha yaxından baxa bilərsiniz . Bizi ikinci hissə, yəni OCP maraqlandırır. Və "∫" da testlər var. Bu kitabda həm suallar, həm də izahatlarla cavablar var. Məsələn:
Çoxları deməyə başlaya bilər ki, bu, üsulların növbəti əzbərlənməsidir. Bir tərəfdən, bəli. Digər tərəfdən, bu suala
ExecutorService
bunun bir növ "təkmilləşdirmə" olduğunu xatırlamaqla cavab vermək olar
Executor
. Və
Executor
sadəcə olaraq mövzuların yaradılması metodunu gizlətmək üçün nəzərdə tutulmuşdur, lakin onları yerinə yetirməyin əsas yolu deyil, yəni yeni bir ipdə işləmək
Runnable
. Ona görə də
execute(Callable)
yox, çünki onlar sadəcə geri dönə bilən
ExecutorService
üsullar əlavə etdilər . Gördüyünüz kimi, metodların siyahısını yadda saxlaya bilərik, lakin dərslərin təbiətini bilsək, təxmin etmək daha asandır. Yaxşı, mövzu ilə bağlı bəzi əlavə materiallar:
Executor
submit
Future
#Viaçeslav
GO TO FULL VERSION