موضوع جي رابطي جي خاصيتن جو هڪ مختصر جائزو. اڳي، اسان ڏٺو ته ڪيئن سلسلا هڪ ٻئي سان هم وقت ساز ٿين ٿا. هن ڀيري اسان انهن مسئلن تي غور ڪنداسين جيڪي پيدا ٿي سگهن ٿا جڏهن ٿريڊس پاڻ ۾ ڳالهائن ٿا ۽ انهن بابت ڳالهائينداسين ته انهن کان ڪيئن بچي سگهجي ٿو. اسان گہرے مطالعي لاءِ ڪجھ مفيد لنڪ پڻ فراهم ڪنداسين.
هڪ بهترين مثال هتي ملي سگهي ٿو: " جاوا - ٿريڊ اسٽارويشن ۽ فيئرنس ". هي مثال ڏيکاري ٿو ته ٿريڊس ڪيئن ڪم ڪن ٿيون Starvation ۾ ۽ ڪيئن Thread.sleep کان Thread.wait ۾ هڪ ننڍڙي تبديلي لوڊ کي هڪجهڙائي سان ورهائي سگهي ٿي.
اهو شايد بهتر آهي ته هي وڊيو ان بابت ڪجهه نه ٻڌائي. تنهنڪري مان صرف وڊيو جي لنڪ ڇڏيندس. توھان پڙھي سگھو ٿا " جاوا - سمجھڻ ھپين- اڳ ۾ رشتا ".
تعارف
تنهن ڪري، اسان ڄاڻون ٿا ته جاوا ۾ اهڙا موضوع آهن، جن بابت توهان جائزو ۾ پڙهي سگهو ٿا " Tread Can't Spoil Java: Part I - Threads " ۽ اهي موضوع هڪ ٻئي سان هم وقت ساز ٿي سگهن ٿا، جن کي اسان جائزو ۾ پيش ڪيو آهي. موضوع جاوا کي خراب نٿو ڪري سگھي ” خراب: حصو II - هم وقت سازي . اهو وقت ڳالهائڻ جو وقت آهي ته موضوع ڪيئن هڪ ٻئي سان لهه وچڙ ۾. اهي عام وسيلن کي ڪيئن حصيداري ڪندا آهن؟ هن سان ڪهڙو مسئلو ٿي سگهي ٿو؟تعطل
سڀ کان وڏو مسئلو Deadlock آهي. جڏهن ٻه يا وڌيڪ سلسلا هميشه لاءِ هڪ ٻئي جو انتظار ڪندا آهن، ان کي Deadlock چئبو آهي. اچو ته " Deadlock " جي تصور جي وضاحت مان Oracle ويب سائيٽ مان هڪ مثال وٺون :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();
}
}
هتي ڊيڊ لاڪ شايد پهريون ڀيرو ظاهر نه ٿئي، پر جيڪڏهن توهان جي پروگرام جي عمل کي روڪيو ويو آهي، اهو هلائڻ جو وقت آهي jvisualvm
: جيڪڏهن هڪ پلگ ان JVisualVM ۾ نصب ٿيل آهي (ذريعي اوزار -> پلگ ان)، اسان ڏسي سگهون ٿا ته ڪٿي تعطل واقع ٿيو:
"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
ٿريڊ 1 ٿريڊ 0 مان بند ٿيڻ جو انتظار ڪري رهيو آهي. ائين ڇو ٿو ٿئي؟ Thread-1
عمل شروع ٿئي ٿو ۽ طريقي سان عمل ڪري ٿو Friend#bow
. اهو لفظ سان نشان لڳل آهي synchronized
، اهو آهي، اسان مانيٽر کي چونڊيندا آهيون this
. طريقي جي داخلا تي، اسان کي هڪ ٻي لنڪ ملي ٿي Friend
. ھاڻي ھي ٿريڊ Thread-1
ڪنھن ٻئي طريقي تي عمل ڪرڻ گھري ٿو Friend
، ان ڪري ھن کان لاڪ پڻ حاصل ڪري ٿو. پر جيڪڏهن ٻئي سلسلي (هن صورت ۾ Thread-0
) جو طريقو داخل ڪرڻ لاء منظم bow
، پوء تالا اڳ ۾ ئي مصروف ۽ Thread-1
انتظار آهي Thread-0
، ۽ ان جي برعڪس. بلاڪ ڪرڻ ناقابل حل آهي، تنهنڪري اهو مئل آهي، اهو آهي، مئل. ٻئي موت جي گرفت (جنهن کي آزاد نه ٿو ڪري سگهجي) ۽ هڪ مئل بلاڪ جنهن کان ڪو به فرار نه ٿي سگهي. Deadlock جي موضوع تي، توهان وڊيو ڏسي سگهو ٿا: " Deadlock - Concurrency #1 - Advanced Java ".
چريو
جيڪڏهن ڪو Deadlock آهي، ته پوء اتي هڪ Livelock آهي؟ ها، اتي آهي) لائيو لاڪ (Livelock) اهو آهي ته ڌاڙا ٻاهران زنده نظر اچن ٿا، پر ساڳئي وقت اهي ڪجهه به نٿا ڪري سگهن، ڇاڪاڻ ته ... جنهن حالت ۾ اهي پنهنجو ڪم جاري رکڻ جي ڪوشش ڪري رهيا آهن انهن کي پورو نٿو ڪري سگهجي. جوهر ۾، Livelock Deadlock سان ملندڙ جلندڙ آهي، پر سلسلا مانيٽر جي انتظار ۾ سسٽم تي "هنگ" نٿا ڪن، پر هميشه ڪجهه ڪري رهيا آهن. مثال طور: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();
}
}
هن ڪوڊ جي ڪاميابي ان ترتيب تي منحصر آهي جنهن ۾ جاوا ٿريڊ شيڊيولر ٿريڊز کي شروع ڪري ٿو. جيڪڏهن اهو پهريون ڀيرو شروع ٿئي ٿو Thead-1
، اسان حاصل ڪنداسين 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
...
جيئن ته مثال مان ڏسي سگهجي ٿو، ٻنهي موضوعن کي متبادل طور تي ٻنهي تالن کي پڪڙڻ جي ڪوشش ڪئي، پر اهي ناڪام ٿيا. ان کان سواء، اهي تعطل ۾ نه آهن، اهو آهي، بصري طور تي انهن سان هر شيء ٺيڪ آهي ۽ اهي پنهنجو ڪم ڪري رهيا آهن. JVisualVM جي مطابق، اسان ننڊ جي دورن ۽ پارڪ جي مدت کي ڏسون ٿا (اهو آهي جڏهن هڪ موضوع هڪ تالا تي قبضو ڪرڻ جي ڪوشش ڪري ٿو، اهو پارڪ اسٽيٽ ۾ وڃي ٿو، جيئن اسان اڳ ۾ بحث ڪيو جڏهن موضوع جي هم وقت سازي بابت ڳالهايو ويو ). Livelock جي موضوع تي، توهان هڪ مثال ڏسي سگهو ٿا: " Java - Thread Livelock ".
بُک
بلاڪ ڪرڻ کان علاوه (deadlock ۽ livelock)، اتي هڪ ٻيو مسئلو آهي جڏهن multithreading سان ڪم ڪري رهيو آهي - Starvation، يا "starvation". اهو رجحان بلاڪ ڪرڻ کان مختلف آهي ته موضوعن کي بلاڪ نه ڪيو ويو آهي، پر انهن وٽ صرف هر ڪنهن لاء ڪافي وسيلا نه آهن. تنهن ڪري، جڏهن ته ڪجهه سلسلا سڄي عمل جي وقت تي قبضو ڪن ٿا، ٻين تي عمل نه ٿو ڪري سگهجي:https://www.logicbig.com/
نسل جي حالت
جڏهن multithreading سان ڪم ڪري، اتي هڪ "ريس شرط" جي طور تي هڪ شيء آهي. اهو رجحان حقيقت ۾ آهي ته موضوع پاڻ ۾ هڪ خاص وسيلا حصيداري ڪندا آهن ۽ ڪوڊ اهڙي طرح لکيو ويو آهي ته اهو هن معاملي ۾ صحيح آپريشن لاء مهيا نٿو ڪري. اچو ته هڪ مثال ڏسو: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();
}
}
هي ڪوڊ شايد پهريون ڀيرو غلطي پيدا نه ڪري سگھي. ۽ اهو شايد هن وانگر نظر اچي ٿو:
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)
جئين توهان ڏسي سگهو ٿا، جڏهن اهو لڳايو ويو هو، newValue
ڪجهه غلط ٿي ويو ۽ newValue
اتي وڌيڪ هئا. ريس اسٽيٽ ۾ ڪجهه سلسلا value
انهن ٻن ٽيمن جي وچ ۾ تبديل ٿيڻ ۾ ڪامياب ٿي ويا. جيئن اسان ڏسي سگهون ٿا، سلسلي جي وچ ۾ هڪ نسل ظاهر ٿيو آهي. هاڻي تصور ڪريو ته اهو ڪيترو ضروري آهي ته پئسن جي ٽرانزيڪشن سان اهڙيون غلطيون نه ڪيون وڃن... مثال ۽ ڊائريگرام پڻ هتي ملي سگهن ٿا: “ جاوا ٿريڊ ۾ نسل جي حالت کي ترتيب ڏيڻ لاءِ ڪوڊ ”.
اُٿلندڙ
موضوعن جي رابطي جي باري ۾ ڳالهائيندي، اهو خاص طور تي لفظ کي ڌيان ڏيڻ جي قابل آهيvolatile
. اچو ته هڪ سادي مثال ڏسو:
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;
}
}
سڀ کان وڌيڪ دلچسپ ڳالهه اها آهي ته هڪ اعلي درجي جي امڪان سان اهو ڪم نه ڪندو. نئين سلسلي ۾ تبديلي نظر نه ايندي flag
. هن کي درست ڪرڻ لاءِ، flag
توهان کي فيلڊ لاءِ ڪي لفظ بيان ڪرڻ جي ضرورت آهي volatile
. ڪيئن ۽ ڇو؟ سڀ عمل پروسيسر طرفان ڪيا ويا آهن. پر حساب ڪتاب جي نتيجن کي ڪٿي ذخيرو ڪرڻ جي ضرورت آهي. هن مقصد لاء، پروسيسر تي مکيه ميموري ۽ هارڊويئر ڪيش آهي. اهي پروسيسر ڪيش ياداشت جي هڪ ننڍڙي ٽڪري وانگر آهن ڊيٽا تائين رسائي حاصل ڪرڻ لاءِ تيزيءَ سان مکيه ميموري تائين رسائي. پر هر شيء ۾ پڻ هڪ منفي آهي: ڪيش ۾ ڊيٽا موجوده نه ٿي سگهي ٿي (جيئن مٿي ڏنل مثال ۾، جڏهن پرچم جي قيمت اپڊيٽ نه ڪئي وئي هئي). تنهن ڪري، لفظ volatile
JVM کي ٻڌائي ٿو ته اسان اسان جي متغير کي ڪيش ڪرڻ نٿا چاهيون. اهو توهان کي سڀني موضوعن ۾ حقيقي نتيجو ڏسڻ جي اجازت ڏئي ٿو. هي هڪ تمام آسان فارموليشن آهي. هن موضوع تي، اهو انتهائي صلاح ڏني وئي آهي ته " JSR 133 (Java Memory Model) FAQvolatile
" جو ترجمو پڙهو . مان توهان کي مواد جي باري ۾ وڌيڪ پڙهڻ جي صلاح ڏيان ٿو “ جاوا ميموري ماڊل ” ۽ “ جاوا وولٽائل ڪي ورڊ ”. ان کان علاوه، اهو ياد رکڻ ضروري آهي ته اهو ڏسڻ جي باري ۾ آهي، ۽ تبديلين جي جوهر بابت نه. جيڪڏهن اسان "ريس جي حالت" مان ڪوڊ وٺون ٿا، اسان IntelliJ Idea ۾ هڪ اشارو ڏسنداسين: هي انسپيڪشن (انسپيڪشن) IntelliJ Idea ۾ شامل ڪيو ويو مسئلو IDEA-61117 جي حصي جي طور تي ، جيڪو 2010 ۾ رليز نوٽس ۾ درج ڪيو ويو هو . volatile
ايٽمي
ايٽمي آپريشنز اهي آپريشن آهن جن کي ورهائي نٿو سگهجي. مثال طور، متغير کي قيمت تفويض ڪرڻ جو عمل ايٽمي آهي. بدقسمتي سان، واڌارو هڪ ايٽمي آپريشن ناهي، ڇاڪاڻ ته وڌ ۾ وڌ ٽن عملن جي ضرورت آهي: پراڻي قدر حاصل ڪريو، ان ۾ ھڪڙو شامل ڪريو، ۽ قيمت بچايو. ايٽمي اهميت ڇو آهي؟ واڌ جي مثال ۾، جيڪڏهن هڪ نسل جي حالت ٿئي ٿي، ڪنهن به وقت تي حصيداري وسيلن (يعني، گڏيل قدر) اوچتو تبديل ٿي سگهي ٿي. ان کان علاوه، اهو ضروري آهي ته 64-bit جوڙجڪ پڻ ايٽمي نه آهن، مثال طورlong
۽ double
. توھان وڌيڪ پڙھي سگھوٿا ھتي: " 64-bit ويلز پڙھڻ ۽ لکڻ وقت ايٽمي کي يقيني بڻايو وڃي ". ايٽمي سان مسئلن جو هڪ مثال هيٺ ڏنل مثال ۾ ڏسي سگهجي ٿو:
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());
}
}
ايٽمي سان ڪم ڪرڻ لاء هڪ خاص طبقو Integer
هميشه اسان کي 30000 ڏيکاريندو، پر value
اهو وقت وقت تي تبديل ٿيندو. هن موضوع تي هڪ مختصر جائزو آهي " جاوا ۾ ايٽمي تبديلين جو هڪ تعارف ". ايٽمي جو بنياد Compare-and-Swap algorithm تي آهي. توھان ان جي باري ۾ وڌيڪ پڙھي سگھوٿا Habré تي مضمون ۾ " Lock-free algorithms - CAS ۽ FAA جو مثال استعمال ڪندي JDK 7 ۽ 8 " يا وڪيپيڊيا تي مضمون ۾ " تبادلي سان مقابلو " بابت.
http://jeremymanson.blogspot.com/2008/11/what-volatile-means-in-java.html
اڳي ٿئي ٿو
اتي هڪ دلچسپ ۽ پراسرار شيء آهي - اڳ ۾ ٿئي ٿو. وهڪري بابت ڳالهائيندي، اهو پڻ ان جي باري ۾ پڙهڻ جي لائق آهي. The Happens Before Relation انهي ترتيب کي ظاهر ڪري ٿو جنهن ۾ ٿريڊن جي وچ ۾ عمل ڏٺو ويندو. ڪيتريون ئي تشريحون ۽ تشريحون آهن. هن موضوع تي تازو رپورٽن مان هڪ آهي هي رپورٽ:نتيجا
هن جائزي ۾، اسان ڌاڳو رابطي جي خاصيتن تي غور ڪيو. اسان انهن مسئلن تي بحث ڪيو جيڪي پيدا ٿي سگهن ٿا ۽ انهن کي ڳولڻ ۽ ختم ڪرڻ جا طريقا. موضوع تي اضافي مواد جي فهرست:- هڪ ڀيرو ٻيهر ڊبل چيڪ ٿيل لاڪنگ بابت
- JSR 133 (جاوا ميموري ماڊل) FAQ (ترجمو)
- ترقي يافته جاوا - اتفاق (Yuri Tkach)
- ڊگلس هاڪنس (2017) پاران جاوا ۾ گڏيل تصورات
GO TO FULL VERSION