Bevezetés
Érdekes dolog a szál. A korábbi áttekintésekben megvizsgáltunk néhány elérhető eszközt a többszálú feldolgozás megvalósításához. Lássuk, milyen érdekes dolgokat tehetünk még. Ezen a ponton sok mindent tudunk. Például a "
Jobb együtt: Java és a szál osztály. I. rész – Végrehajtási szálak " alapján tudjuk, hogy a Thread osztály egy végrehajtási szálat képvisel. Tudjuk, hogy egy szál végrehajt valamilyen feladatot. Ha azt akarjuk, hogy a feladataink képesek legyenek
run
, akkor a szálat a -val kell megjelölnünk
Runnable
.
![Jobb együtt: Java és a Thread osztály. VI. rész – Tüzet el! - 1]()
Emlékeztetni kell arra, hogy használhatjuk a
Tutorialspoint Online Java fordítót :
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();
}
Azt is tudjuk, hogy van valami, amit zárnak hívnak.
Erről a " Jobb együtt: Java és a szál osztály. II. rész — Szinkronizálás című részből tanultunk . Ha az egyik szál zárolást szerez, akkor egy másik szál, amely megpróbálja megszerezni a zárolást, kénytelen megvárni a zárolás feloldását:
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();
}
}
Azt hiszem, itt az ideje, hogy beszéljünk arról, milyen érdekes dolgokat tehetünk még.
Szemaforok
A legegyszerűbb módja annak, hogy szabályozza, hogy hány szál futhasson egyszerre, egy szemafor. Olyan, mint egy vasúti jelzés. A zöld azt jelenti, hogy tovább. A piros azt jelenti, hogy várni kell. Mit várunk a szemafortól? Hozzáférés. A hozzáféréshez meg kell szereznünk. És ha már nincs szükség a hozzáférésre, akkor el kell adnunk vagy el kell engednünk. Lássuk, hogyan működik ez. Importálnunk kell az
java.util.concurrent.Semaphore
osztályt. Példa:
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);
}
Amint látja, ezek a műveletek (megszerzés és elengedés) segítenek megérteni a szemafor működését. A legfontosabb az, hogy ha hozzá akarunk jutni, akkor a szemafornak pozitív engedélyszámmal kell rendelkeznie. Ez a szám negatív számra inicializálható. És 1-nél több engedélyt is kérhetünk (beszerezhetünk).
CountDownLatch
A következő mechanizmus az
CountDownLatch
. Nem meglepő módon ez egy visszaszámlálós retesz. Itt szükségünk van az osztály megfelelő import utasítására
java.util.concurrent.CountDownLatch
. Olyan ez, mint egy lábverseny, ahol mindenki a rajtvonalnál gyűlik össze. És ha mindenki készen áll, mindenki egyszerre kapja meg az indítójelet és egyszerre indul. Példa:
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();
}
}
Először is először közöljük a reteszt, hogy
countDown()
. A Google a visszaszámlálást úgy definiálja, mint "a számok számlálása fordított sorrendben nullára". És akkor azt mondjuk a retesznek, hogy
await()
, azaz várja meg, amíg a számláló nullává válik. Érdekes módon ez egy egyszeri számláló. A Java dokumentáció azt mondja: "Ha a szálakat ismételten vissza kell számolni ilyen módon, használjon CyclicBarriert". Más szóval, ha újrafelhasználható számlálóra van szüksége, akkor egy másik lehetőségre van szüksége:
CyclicBarrier
.
CyclicBarrier
Ahogy a neve is sugallja,
CyclicBarrier
ez egy "újrafelhasználható" akadály. Importálnunk kell az
java.util.concurrent.CyclicBarrier
osztályt. Nézzünk egy példát:
public static void main(String[] args) throws InterruptedException {
Runnable action = () -> System.out.println("On your mark!");
CyclicBarrier barrier = new CyclicBarrier(3, action);
Runnable task = () -> {
try {
barrier.await();
System.out.println("Finished");
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
};
System.out.println("Limit: " + barrier.getParties());
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
}
Amint látható, a szál futtatja a
await
metódust, azaz vár. Ebben az esetben a gát értéke csökken. A sorompó akkor tekinthető megszakadtnak (
barrier.isBroken()
), ha a visszaszámlálás eléri a nullát. Az akadály visszaállításához meg kell hívnia a
reset()
módszert, amely
CountDownLatch
nem rendelkezik.
Hőcserélő
A következő mechanizmus az Exchanger. Ebben az összefüggésben az Exchange egy olyan szinkronizálási pont, ahol a dolgok megváltoznak, vagy felcserélhetők. Ahogy az várható volt, az an
Exchanger
egy olyan osztály, amely cserét vagy cserét hajt végre. Nézzük a legegyszerűbb példát:
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() + " exchanged with " + withThreadName);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(task).start();
new Thread(task).start();
}
Itt két szálat indítunk. Mindegyikük futtatja a cseremódszert, és megvárja, hogy a másik szál is futtatja a cseremódszert. Ennek során a szálak kicserélik az átadott argumentumokat. Érdekes. Nem emlékeztet valamire? Arra emlékeztet
SynchronousQueue
, ami a középpontjában található
CachedThreadPool
. Az érthetőség kedvéért álljon itt egy példa:
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");
}
A példa azt mutatja, hogy amikor egy új szál indul, az vár, mert a sor üres lesz. Ezután a fő szál behelyezi az "Üzenet" karakterláncot a sorba. Sőt, addig is leáll, amíg ez a karakterlánc meg nem érkezik a sorból. Olvassa el a "
SynchronousQueue vs Exchanger " című részt is, hogy többet megtudjon erről a témáról.
Phaser
A legjobbat a végére tartogattuk –
Phaser
. Importálnunk kell az
java.util.concurrent.Phaser
osztályt. Nézzünk egy egyszerű példát:
public static void main(String[] args) throws InterruptedException {
Phaser phaser = new Phaser();
// By calling the register method, we register the current (main) thread as a party
phaser.register();
System.out.println("Phasecount is " + phaser.getPhase());
testPhaser(phaser);
testPhaser(phaser);
testPhaser(phaser);
// After 3 seconds, we arrive at the barrier and deregister. Number of arrivals = number of registrations = start
Thread.sleep(3000);
phaser.arriveAndDeregister();
System.out.println("Phasecount is " + phaser.getPhase());
}
private static void testPhaser(final Phaser phaser) {
// We indicate that there will be a +1 party on the Phaser
phaser.register();
// Start a new thread
new Thread(() -> {
String name = Thread.currentThread().getName();
System.out.println(name + " arrived");
phaser.arriveAndAwaitAdvance(); // The threads register arrival at the phaser.
System.out.println(name + " after passing barrier");
}).start();
}
A példa azt szemlélteti, hogy a használatakor
Phaser
a sorompó letörik, ha a regisztrációk száma megegyezik a sorompóhoz érkezők számával.
A GeeksforGeeks cikkénekPhaser
elolvasásával jobban megismerheti .
Összegzés
Amint az ezekből a példákból látható, a szálak szinkronizálásának többféle módja van. Korábban megpróbáltam felidézni a többszálú feldolgozás szempontjait. Remélem, a sorozat korábbi részei hasznosak voltak. Vannak, akik azt mondják, hogy a többszálú használathoz vezető út a „Java Concurrency in Practice” című könyvvel kezdődik. Bár 2006-ban adták ki, az emberek azt mondják, hogy a könyv meglehetősen alapvető és még ma is aktuális. Például itt olvashatja a vitát:
Érvényes még a „Java Concurrency In Practice”? . Hasznos elolvasni a beszélgetésben található linkeket is.
Például van egy link a The Well-Grounded Java Developer című könyvre , és külön említést teszünk
a 4. Modern párhuzamosság című fejezetről . Erről a témáról van egy teljes ismertető is:
A „Java párhuzamosság a gyakorlatban” még mindig érvényes a Java 8 korszakában? Ez a cikk javaslatokat is kínál arra vonatkozóan, hogy mit érdemes még elolvasni a téma valódi megértéséhez. Ezek után belenézhetsz egy nagyszerű könyvbe, például az
OCA/OCP Java SE 8 programozói gyakorlati tesztekbe . A második mozaikszó érdekel bennünket: OCP (Oracle Certified Professional). A teszteket a „20. fejezet: Java párhuzamosság” részben találja. Ez a könyv kérdéseket és válaszokat tartalmaz magyarázatokkal együtt. Például:
![Jobb együtt: Java és a Thread osztály. VI. rész – Tüzet el! - 3]()
Sokan azt mondják, hogy ez a kérdés egy újabb példa a módszerek memorizálására. Egyrészt igen. Másrészt megválaszolhatja ezt a kérdést, ha felidézi, hogy ez
ExecutorService
egyfajta "frissítés" a
Executor
. És
Executor
célja, hogy egyszerűen elrejtse a szálak létrehozásának módját, de nem ez a fő módja a végrehajtásuknak, vagyis egy
Runnable
objektum elindítása egy új szálon. Ez az oka annak, hogy nincs
execute(Callable)
– mert
ExecutorService
a -ban
Executor
egyszerűen hozzáadja
submit()
a metódusokat, amelyek visszaadhatnak egy
Future
objektumot. Természetesen megjegyezhetünk egy listát a módszerekről, de sokkal egyszerűbb az órák természetére vonatkozó ismereteink alapján megválaszolni a választ. És itt van néhány további anyag a témában:
Jobb együtt: Java és a Thread osztály. I. rész – A végrehajtás szálai Jobb együtt: Java és a Thread osztály. II. rész – Szinkronizálás Jobb együtt: Java és a Thread osztály. III. rész – Interakció Jobb együtt: Java és a szál osztály. IV. rész – Hívható, jövő és barátok Jobb együtt: Java és a szál osztály. V. rész – Végrehajtó, ThreadPool, Fork/Join
GO TO FULL VERSION