Ringkesan ringkes babagan fitur interaksi benang. Sadurunge, kita ndeleng carane benang disinkronake. Wektu iki kita bakal nyilem menyang masalah sing bisa muncul nalika thread sesambungan lan ngomong babagan carane bisa nyingkiri. Kita uga bakal nyedhiyani sawetara pranala migunani kanggo sinau luwih jero.
Conto super bisa ditemokake ing kene: " Java - Thread Starvation and Fairness ". Conto iki nuduhake carane Utas bisa digunakake ing Kelaparan lan carane siji owah-owahan cilik saka Thread.sleep kanggo Thread.wait bisa disebaraké mbukak roto-roto.
Sampeyan mbokmenawa luwih apik yen video iki ora ngandhani apa-apa. Dadi aku mung bakal ninggalake link menyang video. Sampeyan bisa maca " Java - Understanding Happens-before relationships ".
Pambuka
Dadi, kita ngerti manawa ana benang ing Jawa, sing bisa diwaca ing review " Utas Ora Bisa Ngrusak Jawa: Bagian I - Utas "lan Utas bisa disinkronake karo saben liyane, sing ditangani ing review " Utas Ora Bisa Ngrusak Jawa ” Spoil: Part II - Sinkronisasi ." Iku wektu kanggo pirembagan bab carane Utas sesambungan karo saben liyane. Kepiye cara nuduhake sumber daya umum? Apa masalah bisa ana karo iki?Deadlock
Masalah paling awon yaiku Deadlock. Nalika loro utawa luwih Utas ngenteni ing salawas-lawase kanggo saben liyane, iki disebut Deadlock. Ayo njupuk conto saka situs web Oracle saka deskripsi konsep " Deadlock ":public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public synchronized void bow(Friend bower) {
System.out.format("%s: %s has bowed to me!%n",
this.name, bower.getName());
bower.bowBack(this);
}
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s has bowed back to me!%n",
this.name, bower.getName());
}
}
public static void main(String[] args) {
final Friend alphonse = new Friend("Alphonse");
final Friend gaston = new Friend("Gaston");
new Thread(() -> alphonse.bow(gaston)).start();
new Thread(() -> gaston.bow(alphonse)).start();
}
}
Deadlock ing kene bisa uga ora katon sepisanan, nanging yen eksekusi program sampeyan macet, wektune kanggo mbukak jvisualvm
: Yen plugin diinstal ing JVisualVM (liwat Tools -> Plugins), kita bisa ndeleng ing endi deadlock:
"Thread-1" - Thread t@12
java.lang.Thread.State: BLOCKED
at Deadlock$Friend.bowBack(Deadlock.java:16)
- waiting to lock <33a78231> (a Deadlock$Friend) owned by "Thread-0" t@11
Utas 1 ngenteni kunci saka utas 0. Yagene iki kedadeyan? Thread-1
miwiti eksekusi lan nglakokake metode kasebut Friend#bow
. Iki ditandhani karo tembung kunci synchronized
, yaiku, kita njupuk monitor kanthi this
. Ing ngleboke cara, kita nampa link menyang liyane Friend
. Saiki, utas kasebut Thread-1
pengin nglakokake metode liyane Friend
, saengga entuk kunci saka dheweke uga. Nanging yen thread liyane (ing kasus iki Thread-0
) bisa ngetik cara bow
, banjur kunci wis sibuk lan Thread-1
nunggu Thread-0
, lan kosok balene. Pamblokiran ora bisa ditanggulangi, dadi Mati, yaiku mati. Loro-lorone genggeman pati (sing ora bisa dibebasake) lan blok mati sing ora bisa uwal. Ing topik deadlock, sampeyan bisa nonton video: " Deadlock - Concurrency #1 - Advanced Java ".
Livelock
Yen ana Deadlock, banjur ana Livelock? Ya, ana) Livelock yaiku yen benang katon urip ing njaba, nanging ing wektu sing padha ora bisa nindakake apa-apa, amarga ... kahanan kang lagi nyoba kanggo nerusake karya ora bisa ketemu. Intine, Livelock padha karo deadlock, nanging benang ora "nyumerepi" ing sistem nunggu monitor, nanging tansah nindakake apa-apa. Tuladhane:import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class App {
public static final String ANSI_BLUE = "\u001B[34m";
public static final String ANSI_PURPLE = "\u001B[35m";
public static void log(String text) {
String name = Thread.currentThread().getName(); //like Thread-1 or Thread-0
String color = ANSI_BLUE;
int val = Integer.valueOf(name.substring(name.lastIndexOf("-") + 1)) + 1;
if (val != 0) {
color = ANSI_PURPLE;
}
System.out.println(color + name + ": " + text + color);
try {
System.out.println(color + name + ": wait for " + val + " sec" + color);
Thread.currentThread().sleep(val * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Lock first = new ReentrantLock();
Lock second = new ReentrantLock();
Runnable locker = () -> {
boolean firstLocked = false;
boolean secondLocked = false;
try {
while (!firstLocked || !secondLocked) {
firstLocked = first.tryLock(100, TimeUnit.MILLISECONDS);
log("First Locked: " + firstLocked);
secondLocked = second.tryLock(100, TimeUnit.MILLISECONDS);
log("Second Locked: " + secondLocked);
}
first.unlock();
second.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(locker).start();
new Thread(locker).start();
}
}
Kasuksesan kode iki gumantung saka urutane penjadwal thread Jawa miwiti thread. Yen diwiwiti dhisik Thead-1
, kita bakal entuk Livelock:
Thread-1: First Locked: true
Thread-1: wait for 2 sec
Thread-0: First Locked: false
Thread-0: wait for 1 sec
Thread-0: Second Locked: true
Thread-0: wait for 1 sec
Thread-1: Second Locked: false
Thread-1: wait for 2 sec
Thread-0: First Locked: false
Thread-0: wait for 1 sec
...
Minangka bisa dideleng saka conto, loro Utas gantian nyoba kanggo nangkep loro kunci, nanging padha gagal. Kajaba iku, dheweke ora ana ing deadlock, yaiku, kanthi visual kabeh apik karo dheweke lan nindakake tugase. Miturut JVisualVM, kita ndeleng suwé turu lan periode taman (iki nalika thread nyoba kanggo Occupy kunci, iku dadi menyang negara taman, kita rembugan sadurungé nalika ngomong bab sinkronisasi thread ). Ing topik livelock, sampeyan bisa ndeleng conto: " Java - Thread Livelock ".
keluwen
Saliyane pamblokiran (deadlock lan livelock), ana masalah liyane nalika nggarap multithreading - Keluwen, utawa "keluwen". Fenomena iki beda karo pamblokiran amarga benang ora diblokir, nanging mung ora duwe sumber daya sing cukup kanggo kabeh wong. Mulane, nalika sawetara utas njupuk kabeh wektu eksekusi, liyane ora bisa dieksekusi:https://www.logicbig.com/
Kondisi Balapan
Nalika nggarap multithreading, ana "kondisi balapan". Fenomena iki dumunung ing kasunyatan manawa benang nuduhake sumber daya tartamtu lan kode kasebut ditulis kanthi cara sing ora nyedhiyakake operasi sing bener ing kasus iki. Ayo katon ing conto:public class App {
public static int value = 0;
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
int oldValue = value;
int newValue = ++value;
if (oldValue + 1 != newValue) {
throw new IllegalStateException(oldValue + " + 1 = " + newValue);
}
}
};
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
}
}
Kode iki bisa uga ora ngasilake kesalahan nalika sepisanan. Lan bisa uga katon kaya iki:
Exception in thread "Thread-1" java.lang.IllegalStateException: 7899 + 1 = 7901
at App.lambda$main$0(App.java:13)
at java.lang.Thread.run(Thread.java:745)
Kaya sing sampeyan ngerteni, nalika ditugasake, newValue
ana sing salah lan newValue
ana liyane. Sawetara benang ing kondisi balapan bisa diganti value
ing antarane rong tim kasebut. Kaya sing kita deleng, balapan antarane benang wis muncul. Saiki mbayangno sepira pentinge ora nggawe kesalahan sing padha karo transaksi dhuwit ... Conto lan diagram uga bisa dideleng ing kene: " Kode kanggo simulasi kondisi balapan ing benang Jawa ".
molah malih
Ngomong babagan interaksi benang, utamane kudu dicathet tembung kuncivolatile
. Ayo katon ing conto prasaja:
public class App {
public static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Runnable whileFlagFalse = () -> {
while(!flag) {
}
System.out.println("Flag is now TRUE");
};
new Thread(whileFlagFalse).start();
Thread.sleep(1000);
flag = true;
}
}
Ingkang paling menarik yaiku kanthi kemungkinan sing dhuwur ora bakal bisa digunakake. Utas anyar ora bakal weruh owah-owahan flag
. Kanggo ndandani iki, flag
sampeyan kudu nemtokake tembung kunci kanggo lapangan volatile
. Kepiye lan kenapa? Kabeh tumindak ditindakake dening prosesor. Nanging asil pitungan kudu disimpen ing ngendi wae. Kanggo maksud iki, ana memori utama lan cache hardware ing prosesor. Cache prosesor iki kaya sepotong memori cilik kanggo ngakses data luwih cepet tinimbang ngakses memori utama. Nanging kabeh uga duwe kekurangan: data ing cache bisa uga ora saiki (kaya ing conto ing ndhuwur, nalika nilai gendera ora dianyari). Dadi, tembung kunci kasebut volatile
ngandhani JVM yen kita ora pengin cache variabel kita. Iki ngidini sampeyan ndeleng asil nyata ing kabeh thread. Iki minangka formulasi sing gampang banget. Ing topik iki, volatile
dianjurake banget kanggo maca terjemahan " JSR 133 (Java Memory Model) FAQ ". Aku uga menehi saran supaya maca luwih akeh babagan materi " Model Memori Jawa "lan" Kata Kunci Java Volatile ". Kajaba iku, iku penting kanggo elinga yen volatile
iki babagan visibilitas, lan ora babagan atomicity saka owah-owahan. Yen kita njupuk kode saka "Kahanan Balapan", kita bakal weruh pitunjuk ing IntelliJ Idea: Pemriksaan iki (Inspeksi) ditambahake menyang IntelliJ Idea minangka bagean saka masalah IDEA-61117 , sing kadhaptar ing Cathetan Rilis ing taun 2010.
Atomity
Operasi atom yaiku operasi sing ora bisa dipérang. Contone, operasi menehi nilai kanggo variabel yaiku atom. Sayange, increment dudu operasi atom, amarga Tambah mbutuhake telung operasi: entuk nilai lawas, nambah siji, lan nyimpen nilai kasebut. Apa sebabe atomisitas penting? Ing conto increment, yen ana kahanan lomba, ing sembarang wektu sumber daya sing dienggo bareng (yaiku, nilai bareng) bisa dumadakan ngganti. Kajaba iku, iku penting sing struktur 64-dicokot uga ora atom, contonelong
lan double
. Sampeyan bisa maca liyane ing kene: " Priksa atomicity nalika maca lan nulis nilai 64-bit ". Conto masalah karo atomicity bisa dideleng ing conto ing ngisor iki:
public class App {
public static int value = 0;
public static AtomicInteger atomic = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
value++;
atomic.incrementAndGet();
}
};
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
Thread.sleep(300);
System.out.println(value);
System.out.println(atomic.get());
}
}
Kelas khusus kanggo nggarap atom Integer
bakal tansah nuduhake kita 30000, nanging value
bakal diganti saka wektu kanggo wektu. Ana ringkesan ringkes babagan topik iki " Pengantar Variabel Atom ing Jawa ". Atom adhedhasar algoritma Compare-and-Swap. Sampeyan bisa maca liyane babagan iki ing artikel ing Habré " Comparison of Lock-free algoritma - CAS lan FAA nggunakake conto JDK 7 lan 8 " utawa ing Wikipedia ing artikel babagan " Comparison with exchange ".
http://jeremymanson.blogspot.com/2008/11/what-volatile-means-in-java.html
Mengkono Sadurunge
Ana sing menarik lan misterius - Mengkono Sadurunge. Ngomong babagan aliran, sampeyan uga kudu maca babagan iki. Hubungan sing kedadeyan sadurunge nuduhake urutan tumindak ing antarane benang bakal katon. Ana akeh interpretasi lan interpretasi. Salah sawijining laporan paling anyar babagan topik iki yaiku laporan iki:Asil
Ing review iki, kita ndeleng fitur interaksi benang. Kita ngrembug masalah sing bisa kedadeyan lan cara kanggo ndeteksi lan ngilangi. Dhaptar materi tambahan babagan topik:- Sawise maneh babagan kunci sing dicenthang kaping pindho
- JSR 133 (Java Memory Model) FAQ (terjemahan)
- Jawa Lanjut - Concurrency (Yuri Tkach)
- Concurrency Concepts in Java dening Douglas Hawkins (2017)
GO TO FULL VERSION