JavaRush /جاوا بلاگ /Random-SD /جاوا ۾ تعطل ۽ ان کي منهن ڏيڻ جا طريقا
articles
سطح

جاوا ۾ تعطل ۽ ان کي منهن ڏيڻ جا طريقا

گروپ ۾ شايع ٿيل
جڏهن گھڻن موضوعن واري ايپليڪيشنن کي ترقي ڪندي، هڪ مشڪوڪ اڪثر پيدا ٿئي ٿو: ڇا وڌيڪ اهم آهي ايپليڪيشن جي قابل اعتماد يا ڪارڪردگي آهي. مثال طور، اسان ٿريڊ جي حفاظت لاءِ هم وقت سازي کي استعمال ڪريون ٿا، ۽ انهن حالتن ۾ جتي هم وقت سازي جو حڪم غلط آهي، اسان کي بند ڪري سگهون ٿا. اسان وسيلن جي استعمال کي محدود ڪرڻ لاءِ ٿريڊ پول ۽ سيمفورس پڻ استعمال ڪندا آهيون، ۽ هن ڊيزائن ۾ غلطي وسيلن جي کوٽ جي ڪري بند لاڪ ٿي سگهي ٿي. هن آرٽيڪل ۾ اسين ڳالهائينداسين ته ڪيئن بند ٿيڻ کان بچڻ لاء، انهي سان گڏ ايپليڪيشن جي ڪارڪردگي ۾ ٻيا مسئلا. اسان اهو به ڏسنداسين ته ڪيئن هڪ درخواست اهڙي طريقي سان لکي سگهجي ٿي ته جيئن تعطل جي صورت ۾ بحال ٿي سگهي. جاوا ۾ تعطل ۽ ان کي منهن ڏيڻ جا طريقا - 1Deadlock هڪ اهڙي صورتحال آهي جنهن ۾ ٻه يا وڌيڪ عمل جيڪي ڪجهه وسيلن تي قبضو ڪري رهيا آهن انهن کي حاصل ڪرڻ جي ڪوشش ڪري رهيا آهن ڪي ٻيا وسيلا جيڪي ٻين عملن جي قبضي ۾ آهن ۽ ڪو به عمل انهن وسيلن تي قبضو نٿو ڪري سگهي جنهن کي انهن جي ضرورت آهي ۽ ان جي مطابق، قبضو ڪيل هڪ کي آزاد ڪري ٿو. اھا وصف تمام عام آھي ۽ ان ڪري سمجھڻ مشڪل آھي؛ بھتر سمجھڻ لاءِ، اسين مثالن کي استعمال ڪندي ڊيڊ لاڪ جي قسمن کي ڏسنداسين.

هم وقت سازي جو آرڊر ميوئل لاڪنگ

ھيٺ ڏنل ڪم تي غور ڪريو: توھان کي ھڪڙو طريقو لکڻو پوندو جيڪو ھڪڙي رقم جي ھڪڙي رقم کي ھڪڙي اڪائونٽ کان ٻئي ڏانھن منتقل ڪرڻ لاء ٽرانزيڪشن کي انجام ڏئي ٿو. حل هن طرح نظر اچي سگهي ٿو:
public void transferMoney(Account fromAccount, Account toAccount, Amount amount) throws InsufficientFundsException {
	synchronized (fromAccount) {
		synchronized (toAccount) {
			if (fromAccount.getBalance().compareTo(amount) < 0)
				throw new InsufficientFundsException();
			else {
				fromAccount.debit(amount);
				toAccount.credit(amount);
			}
		}
	}
}
پهرين نظر ۾، هي ڪوڊ بلڪل عام طور تي هم وقت سازي ڪيو ويو آهي؛ اسان وٽ هڪ ايٽمي آپريشن آهي چيڪ ڪرڻ ۽ تبديل ڪرڻ جو ذريعو اڪائونٽ جي حالت ۽ منزل جي اڪائونٽ کي تبديل ڪرڻ. بهرحال، هن هم وقت سازي جي حڪمت عملي سان، هڪ تعطل واري صورتحال ٿي سگهي ٿي. اچو ته هڪ مثال ڏسو ته اهو ڪيئن ٿئي ٿو. اهو ٻه ٽرانزيڪشن ڪرڻ ضروري آهي: اڪائونٽ A کان اڪائونٽ B ڏانهن x پئسا منتقلي، ۽ اڪائونٽ B کان اڪائونٽ A ۾ y پئسا منتقل ڪرڻ. گهڻو ڪري اها صورتحال تعطل جو سبب نه بڻجندي، جڏهن ته، بدقسمتي جي حالتن ۾، ٽرانزيڪشن 1 اڪائونٽ مانيٽر A تي قبضو ڪندو، ٽرانزيڪشن 2 اڪائونٽ مانيٽر B تي قبضو ڪندو. نتيجو هڪ تعطل آهي: ٽرانزيڪشن 1 ٽرانزيڪشن 2 جو انتظار ڪري ٿو اڪائونٽ مانيٽر ڇڏڻ لاءِ B، پر ٽرانزيڪشن 2 کي مانيٽر اي تائين پهچڻ لازمي آهي، جيڪو ٽرانزيڪشن 1 تي قبضو ڪيو ويو آهي. ڊيڊ لاڪ سان گڏ هڪ وڏو مسئلو اهو آهي ته انهن کي جانچ ۾ ڳولڻ آسان ناهي. جيتوڻيڪ مثال ۾ بيان ڪيل صورتحال ۾، موضوعن کي بلاڪ نه ٿي سگھي، اهو آهي، اها صورتحال مسلسل ٻيهر پيدا نه ٿيندي، جيڪا خاص طور تي تشخيص کي پيچيده ڪري ٿي. عام طور تي، غير ارادي جو بيان ڪيل مسئلو ملٽي ٿريڊنگ لاءِ عام آهي (جيتوڻيڪ اهو ان کي آسان نٿو بڻائي). تنهن ڪري، ڪوڊ جو جائزو گھڻن موضوعن واري ايپليڪيشنن جي معيار کي بهتر بڻائڻ ۾ اهم ڪردار ادا ڪري ٿو، ڇاڪاڻ ته اهو توهان کي انهن غلطين کي سڃاڻڻ جي اجازت ڏئي ٿو جيڪي ٽيسٽ دوران ٻيهر پيدا ڪرڻ ڏکيو آهن. اهو، يقينا، مطلب اهو ناهي ته ايپليڪيشن کي آزمائشي ٿيڻ جي ضرورت ناهي؛ اسان کي صرف ڪوڊ جي نظرثاني بابت نه وسارڻ گهرجي. مون کي ڇا ڪرڻ گهرجي ته هن ڪوڊ کي بند ٿيڻ کان روڪڻ لاءِ؟ هي بلاڪنگ ان حقيقت جي ڪري آهي ته اڪائونٽ جي هم وقت سازي مختلف ترتيب ۾ ٿي سگهي ٿي. ان مطابق، جيڪڏهن توهان اڪائونٽس تي ڪجهه آرڊر متعارف ڪرايو ٿا (اهو ڪجهه قاعدو آهي جيڪو توهان کي اهو چوڻ جي اجازت ڏئي ٿو ته اڪائونٽ A اڪائونٽ B کان گهٽ آهي)، پوء مسئلو ختم ٿي ويندو. اهو ڪيئن ڪجي؟ پهرين، جيڪڏهن اڪائونٽن ۾ ڪنهن قسم جو منفرد سڃاڻپ ڪندڙ آهي (مثال طور، هڪ اڪائونٽ نمبر) عددي، ننڍو، يا ٻيو ڪجهه ترتيب جي قدرتي تصور سان (اسٽرنگ کي لغوي ترتيب ۾ مقابلو ڪري سگهجي ٿو، پوء اسان پاڻ کي خوش قسمت سمجهي سگهون ٿا، ۽ اسين ڪنداسين. هميشه اسان پهرين ننڍي اڪائونٽ جي مانيٽر تي قبضو ڪري سگهون ٿا، ۽ پوء وڏو (يا ان جي برعڪس).
private void doTransfer(final Account fromAcct, final Account toAcct, final DollarAmount amount) throws InsufficientFundsException {
	if (fromAcct.getBalance().compareTo(amount) < 0)
		throw new InsufficientFundsException();
	else {
		fromAcct.debit(amount);
		toAcct.credit(amount);
	}
}
public void transferMoney(final Account fromAcct, final Account toAcct, final DollarAmount amount) throws InsufficientFundsException {
	int fromId= fromAcct.getId();
	int toId = fromAcct.getId();
	if (fromId < toId) {
		synchronized (fromAcct) {
			synchronized (toAcct) {
				doTransfer(fromAcct, toAcct, amount)}
			}
		}
	} else  {
		synchronized (toAcct) {
			synchronized (fromAcct) {
				doTransfer(fromAcct, toAcct, amount)}
			}
		}
	}
}
ٻيو اختيار، جيڪڏهن اسان وٽ اهڙو ڪو سڃاڻيندڙ نه آهي، ته اسان کي ان سان گڏ پاڻ وٽ اچڻو پوندو. اسان ڪري سگھون ٿا، پھرين لڳ ڀڳ، شين جو مقابلو ھيش ڪوڊ ذريعي. گهڻو ڪري اهي مختلف هوندا. پر ڇا جيڪڏهن اهي ساڳيون ٿي وڃن؟ پوء توهان کي هم وقت سازي لاء هڪ ٻيو اعتراض شامل ڪرڻو پوندو. اهو ٿورڙو نفيس نظر اچي سگهي ٿو، پر توهان ڇا ڪري سگهو ٿا؟ ۽ ان کان علاوه، ٽيون اعتراض تمام گهٽ استعمال ڪيو ويندو. نتيجو هن طرح نظر ايندو:
private static final Object tieLock = new Object();
private void doTransfer(final Account fromAcct, final Account toAcct, final DollarAmount amount) throws InsufficientFundsException {
	if (fromAcct.getBalance().compareTo(amount) < 0)
		throw new InsufficientFundsException();
	else {
		fromAcct.debit(amount);
		toAcct.credit(amount);
	}
}
public void transferMoney(final Account fromAcct, final Account toAcct, final DollarAmount amount) throws InsufficientFundsException {
	int fromHash = System.identityHashCode(fromAcct);
	int toHash = System.identityHashCode(toAcct);
	if (fromHash < toHash) {
		synchronized (fromAcct) {
			synchronized (toAcct) {
				doTransfer(fromAcct, toAcct, amount);
			}
		}
	} else if (fromHash > toHash) {
		synchronized (toAcct) {
			synchronized (fromAcct) {
				doTransfer(fromAcct, toAcct, amount);
			}
		}
	} else {
		synchronized (tieLock) {
			synchronized (fromAcct) {
				synchronized (toAcct) {
					doTransfer(fromAcct, toAcct, amount)
				}
			}
		}
	}
}

شين جي وچ ۾ بندش

بيان ڪيل بلاڪنگ حالتون تشخيص لاء ڊيڊ لاڪ جي آسان ترين صورت جي نمائندگي ڪن ٿيون. گهڻو ڪري گھڻن موضوعن واري ايپليڪيشنن ۾، مختلف شيون ساڳيون هم وقت سازي بلاڪ تائين رسائي حاصل ڪرڻ جي ڪوشش ڪندا آهن. اهو تعطل جو سبب بڻجي سگهي ٿو. هيٺ ڏنل مثال تي غور ڪريو: هڪ فلائيٽ ڊسپيچر ايپليڪيشن. هوائي جهاز ڪنٽرولر کي ٻڌائين ٿا جڏهن اهي پنهنجي منزل تي پهچي ويا آهن ۽ لينڊ ڪرڻ جي اجازت جي درخواست ڪن ٿا. ڪنٽرولر پنهنجي طرفن ۾ اڏامندڙ جهازن بابت سموري معلومات محفوظ ڪري ٿو ۽ نقشي تي انهن جي پوزيشن کي پلاٽ ڪري سگهي ٿو.
class Plane {
	private Point location, destination;
	private final Dispatcher dispatcher;

	public Plane(Dispatcher dispatcher) {
		this.dispatcher = dispatcher;
	}
	public synchronized Point getLocation() {
		return location;
	}
	public synchronized void setLocation(Point location) {
		this.location = location;
		if (location.equals(destination))
		dispatcher.requestLanding(this);
	}
}

class Dispatcher {
	private final Set<Plane> planes;
	private final Set<Plane> planesPendingLanding;

	public Dispatcher() {
		planes = new HashSet<Plane>();
		planesPendingLanding = new HashSet<Plane>();
	}
	public synchronized void requestLanding(Plane plane) {
		planesPendingLanding.add(plane);
	}
	public synchronized Image getMap() {
		Image image = new Image();
		for (Plane plane : planes)
			image.drawMarker(plane.getLocation());
		return image;
	}
}
سمجھڻ ته ھن ڪوڊ ۾ ھڪڙو بگ آھي جيڪو ختم ٿيڻ جي ڪري سگھي ٿو اڳئين ھڪڙي کان وڌيڪ ڏکيو آھي. پهرين نظر ۾، ان کي ٻيهر هم وقت سازي نه آهي، پر اهو معاملو ناهي. توهان شايد اڳ ۾ ئي محسوس ڪيو آهي ته setLocationڪلاس Plane۽ getMapڪلاس جا طريقا Dispatcherهم وقت ساز ٿين ٿا ۽ پاڻ ۾ ٻين طبقن جي هم وقت سازي طريقن کي سڏين ٿا. اهو عام طور تي خراب عمل آهي. اهو ڪيئن درست ٿي سگهي ٿو ايندڙ سيڪشن ۾ بحث ڪيو ويندو. نتيجي طور، جيڪڏهن جهاز ان هنڌ تي پهچي ٿو، جنهن وقت ڪو ماڻهو ڪارڊ حاصل ڪرڻ جو فيصلو ڪري ٿو، هڪ تعطل پيدا ٿي سگهي ٿو. اهو آهي، getMap۽ طريقن کي سڏيو ويندو، setLocationجيڪو مثال جي نگراني تي قبضو ڪندو Dispatcher۽ Planeترتيب سان. اهو طريقو وري getMapڪال ڪندو plane.getLocation(خاص طور تي ان مثال تي Planeجيڪو هن وقت مصروف آهي) جيڪو مانيٽر جي هر هڪ مثال لاءِ آزاد ٿيڻ جو انتظار ڪندو Plane. ساڳئي وقت، طريقو setLocationسڏيو ويندو dispatcher.requestLanding، جڏهن ته مثال مانيٽر Dispatcherنقشي کي ڊرائنگ ۾ مصروف رهي ٿو. نتيجو هڪ تعطل آهي.

ڪالون کوليو

حالتن کان بچڻ لاءِ جيئن اڳئين حصي ۾ بيان ڪيو ويو آهي، اها سفارش ڪئي وئي آهي ته عوامي ڪالن کي ٻين شين جي طريقن سان استعمال ڪيو وڃي. اهو آهي، هم وقت سازي واري بلاڪ کان ٻاهر ٻين شين جي طريقن کي ڪال ڪريو. جيڪڏهن اوپن ڪالز جي اصول کي استعمال ڪندي طريقن کي ٻيهر لکيو وڃي setLocation، getMapبند ٿيڻ جو امڪان ختم ٿي ويندو. اهو نظر ايندو، مثال طور، هن طرح:
public void setLocation(Point location) {
	boolean reachedDestination;
	synchronized(this){
		this.location = location;
		reachedDestination = location.equals(destination);
	}
	if (reachedDestination)
		dispatcher.requestLanding(this);
}
………………………………………………………………………………
public Image getMap() {
	Set<Plane> copy;
	synchronized(this){
		copy = new HashSet<Plane>( planes);
	}
	Image image = new Image();
	for (Plane plane : copy)
		image.drawMarker(plane.getLocation());
	return image;
}

وسيلن جي بندش

Deadlocks پڻ ٿي سگھي ٿو جڏھن ڪجھ وسيلن تائين رسائي حاصل ڪرڻ جي ڪوشش ڪري رھيا آھن جيڪي ھڪڙي وقت ۾ استعمال ڪري سگھن ٿا. ھڪڙو مثال ھڪڙو ڊيٽابيس ڪنيڪشن پول ھوندو. جيڪڏهن ڪجهه موضوعن کي هڪ ئي وقت ۾ ٻن ڪنيڪشنن تائين رسائي جي ضرورت آهي ۽ اهي انهن کي مختلف آرڊرن ۾ پهچائين ٿا، اهو ٿي سگهي ٿو بند لاڪ. بنيادي طور تي، هن قسم جي لاڪنگ هم وقت سازي آرڊر لاڪنگ کان مختلف ناهي، سواء ان کان سواء اهو نه ٿيندو جڏهن ڪجهه ڪوڊ کي عمل ڪرڻ جي ڪوشش ڪري، پر جڏهن وسيلن تائين رسائي جي ڪوشش ڪري ٿي.

ڪيئن Deadlocks کان بچڻ لاء؟

يقينن، جيڪڏهن ڪوڊ بغير ڪنهن غلطي جي لکيو ويو آهي (جنهن جا مثال اسان اڳئين حصن ۾ ڏٺا)، پوء ان ۾ ڪو به رڪاوٽ نه هوندو. پر ڪير ضمانت ڏئي سگهي ٿو ته هن جو ڪوڊ غلطين کان سواء لکيو ويو آهي؟ يقينن، جاچ غلطين جي هڪ اهم حصي کي سڃاڻڻ ۾ مدد ڪري ٿي، پر جيئن اسان اڳ ۾ ڏٺو آهي، ملٽي ٿريڊڊ ڪوڊ ۾ غلطيون تشخيص ڪرڻ آسان نه هونديون آهن ۽ جاچ ڪرڻ کان پوءِ به توهان پڪ نه ٿا ڪري سگهو ته ڪي به بند حالتون نه آهن. ڇا اسان ڪنهن به طرح پاڻ کي روڪڻ کان بچائي سگهون ٿا؟ جواب آهي ها. ساڳيون ٽيڪنالاجيون ڊيٽابيس انجڻين ۾ استعمال ٿينديون آهن، جن کي اڪثر ڊيڊ لاڪ (ڊيٽابيس ۾ ٽرانزيڪشن ميڪانيزم سان لاڳاپيل) مان بحال ڪرڻ جي ضرورت هوندي آهي. Lockپيڪيج ۾ موجود انٽرفيس ۽ ان تي عمل درآمد java.util.concurrent.locksتوهان کي اجازت ڏئي ٿو ته مانيٽر تي قبضو ڪرڻ جي ڪوشش ڪريو انهي ڪلاس جي هڪ مثال سان لاڳاپيل طريقو استعمال ڪندي tryLock(جيڪڏهن مانيٽر تي قبضو ڪرڻ ممڪن هجي ته صحيح موٽڻ). فرض ڪريو اسان وٽ شيون جو هڪ جوڙو آهي جيڪو هڪ انٽرفيس کي لاڳو ڪري ٿو Lock۽ اسان کي انهن جي مانيٽر تي قبضو ڪرڻ جي ضرورت آهي انهي طريقي سان جيئن باهمي بلاڪنگ کان بچڻ لاء. توھان ان کي ھن طرح لاڳو ڪري سگھو ٿا:
public void twoLocks(Lock A,  Lock B){
	while(true){
		if(A.tryLock()){
			if(B.tryLock())
			{
				try{
					//do something
				} finally{
					B.unlock();
					A.unlock();
				}
			} else{
				A.unlock();
			}
		}
	}
}
جئين توهان هن پروگرام ۾ ڏسي سگهو ٿا، اسان ٻه مانيٽر تي قبضو ڪندا آهيون، جڏهن ته باهمي بلاڪنگ جي امڪان کي ختم ڪندي. مهرباني ڪري نوٽ ڪريو ته بلاڪ try- finallyضروري آهي ڇو ته پيڪيج ۾ ڪلاس java.util.concurrent.locksخودڪار طور تي مانيٽر کي آزاد نه ڪندا آهن، ۽ جيڪڏهن توهان جي ڪم جي عمل دوران ڪجهه استثنا ٿئي ٿي، مانيٽر بند ٿيل حالت ۾ ڦاسي ويندو. تعطل جي تشخيص ڪيئن ڪجي؟ JVM توهان کي ڊيڊ لاڪ جي تشخيص ڪرڻ جي اجازت ڏئي ٿي انهن کي ٿريڊ ڊمپس ۾ ڏيکاريندي. اهڙن ڊمپن ۾ معلومات شامل آهي ته موضوع ڪهڙي حالت ۾ آهي. جيڪڏهن اهو بلاڪ ڪيو ويو آهي، ڊمپ مانيٽر جي باري ۾ معلومات تي مشتمل آهي ته موضوع جاري ٿيڻ جي انتظار ۾ آهي. موضوعن کي ڊمپ ڪرڻ کان اڳ، JVM انتظار (مصروف) مانيٽر جي گراف کي ڏسي ٿو، ۽ جيڪڏهن اهو چڪر ڳولي ٿو، اهو ڊيڊ لاڪ معلومات شامل ڪري ٿو، جيڪو حصو وٺندڙ مانيٽر ۽ موضوعن کي ظاهر ڪري ٿو. بند ٿيل موضوعن جو هڪ ڊمپ هن طرح نظر اچي ٿو:
Found one Java-level deadlock:
=============================
"ApplicationServerThread":
waiting to lock monitor 0x0f0d80cc (a MyDBConnection),
which is held by "ApplicationServerThread"
"ApplicationServerThread":
waiting to lock monitor 0x0f0d8fed (a MyDBCallableStatement),
which is held by "ApplicationServerThread"
Java stack information for the threads listed above:
"ApplicationServerThread":
at MyDBConnection.remove_statement
- waiting to lock <0x6f50f730> (a MyDBConnection)
at MyDBStatement.close
- locked <0x604ffbb0> (a MyDBCallableStatement)
...
"ApplicationServerThread":
at MyDBCallableStatement.sendBatch
- waiting to lock <0x604ffbb0> (a MyDBCallableStatement)
at MyDBConnection.commit
- locked <0x6f50f730> (a MyDBConnection)
مٿيون ڊمپ واضح طور تي ڏيکاري ٿو ته ڊيٽابيس سان ڪم ڪندڙ ٻه موضوع هڪ ٻئي کي بلاڪ ڪيو آهي. ھن JVM خصوصيت کي استعمال ڪندي ڊيڊ لاڪ جي تشخيص ڪرڻ لاءِ، پروگرام ۾ مختلف هنڌن تي ٿريڊ ڊمپ آپريشن کي ڪال ڪرڻ ۽ ايپليڪيشن کي جانچڻ ضروري آھي. اڳيون، توهان کي نتيجن جي لاگن جو تجزيو ڪرڻ گهرجي. جيڪڏهن اهي ظاهر ڪن ٿا ته هڪ تعطل پيدا ٿيو آهي، ڊمپ مان معلومات انهن حالتن کي ڳولڻ ۾ مدد ڪندي جنهن جي تحت اهو واقع ٿيو. عام طور تي، توهان کي انهن حالتن کان پاسو ڪرڻ گهرجي جيئن انهن کي ختم ڪرڻ جي مثالن ۾. اهڙين حالتن ۾، ايپليڪيشن گهڻو ڪري مستحڪم طور تي ڪم ڪندي. پر نه وساريو جاچ ۽ ڪوڊ جائزو جي باري ۾. اهو مسئلو جي نشاندهي ڪرڻ ۾ مدد ڪندو جيڪڏهن اهي واقع ٿين ٿيون. انهن حالتن ۾ جتي توهان هڪ سسٽم ٺاهي رهيا آهيو جنهن لاءِ ڊيڊ لاڪ فيلڊ جي بحالي انتهائي اهم آهي، توهان استعمال ڪري سگهو ٿا سيڪشن ۾ بيان ڪيل طريقو ”ڊڊ لاڪ کان ڪيئن بچجي؟“. انهي حالت ۾، lockInterruptiblyانٽرفيس جو Lockطريقو java.util.concurrent.locks. اهو توهان کي ان سلسلي ۾ مداخلت ڪرڻ جي اجازت ڏئي ٿو جيڪو هن طريقي سان استعمال ڪندي مانيٽر تي قبضو ڪيو آهي (۽ اهڙي طرح مانيٽر کي آزاد ڪريو).
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION