Panimula
Ang mga stream ay isang kawili-wiling bagay. Sa mga nakaraang pagsusuri, tiningnan namin ang ilan sa mga magagamit na tool para sa pagpapatupad ng multithreading. Tingnan natin kung ano pa ang mga kawili-wiling bagay na maaari nating gawin. Sa puntong ito marami tayong alam. Halimbawa, mula sa “
You Can't Spoil Java with a Thread: Part I - Threads, " alam namin na ang isang thread ay isang Thread. Alam namin na ang isang thread ay nagsasagawa ng ilang gawain. Kung gusto nating tumakbo ang ating gawain (
run
), dapat nating tukuyin ang thread upang maging tiyak
Runnable
.
Upang matandaan, maaari naming gamitin
ang Tutorialspoint Java Online Compiler :
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();
}
Alam din natin na mayroon tayong ganitong konsepto bilang lock. Nabasa namin ang tungkol dito sa "
You Can't Spoil Java with a Thread: Part II - Synchronization ." Maaaring sakupin ng isang thread ang isang lock at pagkatapos ay ang isa pang thread na sumusubok na sakupin ang kandado ay mapipilitang maghintay para maging libre ang lock:
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();
}
}
Sa tingin ko oras na para pag-usapan kung ano pa ang magagawa natin na kawili-wili.
Mga semaphore
Ang pinakasimpleng paraan ng pagkontrol kung gaano karaming mga thread ang maaaring gumana nang sabay-sabay ay isang semaphore. Parang sa riles. Naka-on ang berdeng ilaw - maaari mo. Bukas ang pulang ilaw - naghihintay kami. Ano ang inaasahan natin mula sa isang semaphore? Mga Pahintulot. Pahintulot sa Ingles - permit. Upang makakuha ng pahintulot, kailangan mong makuha ito, na sa Ingles ay makukuha. At kapag hindi na kailangan ng permiso, kailangan na natin itong ibigay, ibig sabihin, bitawan o tanggalin, na sa English ay ilalabas. Tingnan natin kung paano ito gumagana. Kakailanganin nating i-import ang klase
java.util.concurrent.Semaphore
. Halimbawa:
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);
}
Tulad ng nakikita natin, na naisaulo ang mga salitang Ingles, naiintindihan natin kung paano gumagana ang semaphore. Kapansin-pansin, ang pangunahing kondisyon ay ang semaphore na "account" ay dapat na may positibong bilang ng mga permit. Samakatuwid, maaari mong simulan ito ng isang minus. At maaari kang humiling (makakuha) ng higit sa 1.
CountDownLatch
Ang susunod na mekanismo ay
CountDownLatch
. Ang CountDown sa English ay isang countdown, at ang Latch ay isang bolt o latch. Ibig sabihin, kung isasalin natin ito, ito ay isang latch na may countdown. Dito kailangan natin ang naaangkop na pag-import ng klase
java.util.concurrent.CountDownLatch
. Ito ay parang karera o karera kung saan lahat ay nagtitipon sa panimulang linya at kapag handa na ang lahat, binibigyan ng pahintulot at lahat ay nagsisimula nang sabay-sabay. Halimbawa:
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();
}
}
maghintay sa English - to expect. Ibig sabihin, mag-usap muna kami
countDown
. Gaya ng sabi ng Google Translator, ang count down ay "isang pagkilos ng pagbibilang ng mga numero sa reverse order sa zero," ibig sabihin, magsagawa ng countdown action, ang layunin nito ay magbilang hanggang zero. At pagkatapos ay sinasabi namin
await
- iyon ay, maghintay hanggang ang counter value ay maging zero. Ito ay kagiliw-giliw na ang naturang counter ay disposable. Tulad ng sinabi sa JavaDoc - "Kapag ang mga thread ay dapat na paulit-ulit na bilangin pababa sa paraang ito, sa halip ay gumamit ng isang CyclicBarrier", iyon ay, kung kailangan mo ng reusable counting, kailangan mong gumamit ng isa pang opsyon, na tinatawag na
CyclicBarrier
.
CyclicBarrier
Gaya ng ipinahihiwatig ng pangalan,
CyclicBarrier
ito ay isang paikot na hadlang. Kakailanganin nating i-import ang klase
java.util.concurrent.CyclicBarrier
. Tingnan natin ang isang halimbawa:
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();
}
}
Tulad ng nakikita mo, ang thread ay nagpapatupad
await
, iyon ay, naghihintay. Sa kasong ito, bumababa ang halaga ng hadlang. Ang hadlang ay itinuturing na sira (
berrier.isBroken()
) kapag ang countdown ay umabot sa zero. Upang i-reset ang hadlang, kailangan mong tawagan ang
berrier.reset()
, na nawawala sa
CountDownLatch
.
Exchanger
Ang susunod na lunas ay
Exchanger
. Ang palitan mula sa Ingles ay isinalin bilang exchange o exchange. Ang A
Exchanger
ay isang exchanger, iyon ay, isang bagay kung saan sila nakikipagpalitan. Tingnan natin ang isang simpleng halimbawa:
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();
}
Dito naglulunsad kami ng dalawang thread. Ang bawat isa sa kanila ay nagpapatupad ng paraan ng palitan at naghihintay para sa isa pang thread na isakatuparan din ang paraan ng palitan. Kaya, ang mga thread ay magpapalitan ng mga naipasa na argumento sa kanilang mga sarili. Kawili-wiling bagay. Wala ba siyang naaalala sa iyo? At pinaalalahanan niya si
SynchronousQueue
, na nasa puso ng
cachedThreadPool
'a. Para sa kalinawan, narito ang isang halimbawa:
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");
}
Ipinapakita ng halimbawa na sa pamamagitan ng paglulunsad ng bagong thread, mapupunta ang thread na ito sa waiting mode, dahil mawawalan ng laman ang pila. At pagkatapos
main
ay i-queue ng thread ang text na "Mensahe". Kasabay nito, hihinto ito sa kinakailangang oras hanggang sa matanggap nito ang elemento ng teksto mula sa pila. Sa paksang ito maaari mo ring basahin ang "
SynchronousQueue Vs Exchanger ".
Phaser
At sa wakas, ang pinakamatamis na bagay -
Phaser
. Kakailanganin nating i-import ang klase
java.util.concurrent.Phaser
. Tingnan natin ang isang simpleng halimbawa:
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();
}
Ang halimbawa ay nagpapakita na ang hadlang, kapag gumagamit ng
Phaser
'a, ay nasira kapag ang bilang ng mga pagpaparehistro ay tumutugma sa bilang ng mga dumating sa hadlang. Maaari mong malaman ang higit pa
Phaser
sa artikulo mula sa hub na "
New Phaser synchronizer ".
Mga resulta
Tulad ng nakikita mo mula sa mga halimbawa, may iba't ibang paraan upang i-synchronize ang mga thread. Kanina sinubukan kong alalahanin ang tungkol sa multithreading, sana ay kapaki-pakinabang ang mga naunang bahagi. Sinasabi nila na ang landas sa multithreading ay nagsisimula sa aklat na "Java Concurrency in Practice". Bagama't ito ay lumabas noong 2006, ang mga tao ay tumugon na ang aklat ay napaka-pangunahing at pa rin pack ng isang suntok. Halimbawa, maaari mong basahin ang mga talakayan dito: "
Wasto pa rin ba ang Java Concurrency In Practice? ". Nakatutulong din na basahin ang mga link mula sa talakayan. Halimbawa, mayroong isang link sa aklat na "
The Well-Grounded Java Developer ", kung saan ito ay nagkakahalaga ng pagbibigay pansin sa "
Kabanata 4. Modern concurrency ". May isa pang buong pagsusuri sa parehong paksa: "
May kaugnayan pa ba ang Java cocurrency sa pracitce sa panahon ng java 8 ". Mayroon din itong mga tip kung ano pa ang dapat mong basahin upang talagang maunawaan ang paksa. Pagkatapos nito, maaari mong tingnang mabuti ang napakagandang aklat gaya ng "
OCA OCP JavaSE 8 Programmer Practice Tests ". Interesado kami sa pangalawang bahagi, iyon ay, OCP. At may mga pagsubok sa "∫". Ang aklat na ito ay naglalaman ng parehong mga tanong at sagot na may mga paliwanag. Halimbawa:
Marami ang maaaring magsimulang magsabi na isa lamang itong pagsasaulo ng mga pamamaraan. Sa isang banda, oo. Sa kabilang banda, masasagot ang tanong na ito sa pamamagitan ng pag-alala na
ExecutorService
ito ay isang uri ng "pag-upgrade"
Executor
. At
Executor
ito ay inilaan lamang upang itago ang paraan ng paglikha ng mga thread, ngunit hindi ang pangunahing paraan ng pagpapatupad ng mga ito, iyon ay, tumatakbo sa isang bagong thread
Runnable
. Samakatuwid,
execute(Callable)
hindi, dahil nagdagdag lamang sila ng mga pamamaraan
ExecutorService
na maaaring bumalik . Tulad ng nakikita mo, maaari naming kabisaduhin ang isang listahan ng mga pamamaraan, ngunit mas madaling hulaan kung alam namin ang likas na katangian ng mga klase mismo. Well, ilang karagdagang mga materyales sa paksa:
Executor
submit
Future
#Viacheslav
GO TO FULL VERSION