JavaRush /Java Blog /Random-TK /Java-da petiklemek we oňa garşy göreş usullary
articles
Dereje

Java-da petiklemek we oňa garşy göreş usullary

Toparda çap edildi
Köp sapakly programmalar işlenip düzülende köplenç bir dilemma ýüze çykýar: has möhümi programmanyň ygtybarlylygy ýa-da öndürijiligi. Mysal üçin, sapaklaryň howpsuzlygy üçin sinhronizasiýany ulanýarys we sinhronizasiýa tertibi nädogry bolan ýagdaýynda, kynçylyklara sebäp bolup bileris. Şeýle hem, çeşmeleriň sarp edilmegini çäklendirmek üçin sapak howuzlaryny we semaforlary ulanýarys we bu dizaýndaky ýalňyşlyk çeşmeleriň ýoklugy sebäpli petiklenip biler. Bu makalada, programmanyň ýerine ýetirilişindäki beýleki kynçylyklar ýaly petikden nädip gaça durmalydygy barada söhbet ederis. Şeýle hem, haýsydyr bir programmanyň petiklenen ýagdaýynda dikeldilmegi üçin nädip ýazylyp bilinjekdigine serederis. Java-da petiklemek we oňa garşy göreş usullary - 1“Deadlock”, käbir çeşmeleri eýeleýän iki ýa-da has köp prosessiň beýleki amallar bilen meşgullanýan käbir beýleki çeşmeleri edinmäge synanyşýan we hiç bir proses zerur çeşmäni alyp bilmeýän we şoňa görä-de basyp alnan birini boşadyp bilýän ýagdaýdyr. Bu kesgitleme gaty umumy we şonuň üçin düşünmek kyn; has gowy düşünmek üçin mysallaryň kömegi bilen möhletleriň görnüşlerine serederis.

Sinhronizasiýa buýrugy özara gulplamak

Aşakdaky meseläni gözden geçiriň: belli bir mukdarda pullary bir hasapdan beýlekisine geçirmek üçin geleşik amala aşyrýan usul ýazmaly. Çözüw şeýle bolup biler:
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);
			}
		}
	}
}
Bir seretseň, bu kod adaty ýagdaýda sinhronlanýar; çeşme hasabynyň ýagdaýyny barlamak we üýtgetmek we barjak hasabyňyzy üýtgetmek boýunça atom operasiýamyz bar. Şeýle-de bolsa, bu sinhronizasiýa strategiýasy bilen petikli ýagdaý ýüze çykyp biler. Munuň nähili bolýandygyna bir mysal göreliň. Iki amal etmeli: x hasabyny A hasabyndan B hasabyna geçirmek, y hasabyny B hasabyndan A hasabyna geçirmek. Köplenç bu ýagdaý petiklenmä sebäp bolmaz, ýöne, gynansak-da, köp ýagdaýlarda 1-nji amal A hasabynyň monitoryny, 2-nji amal hasabyň monitoryny eýelär. Netije petiklenýär: 1-nji amal 2-nji amal hasabynyň monitoryny goýbermegine garaşýar B, ýöne 2-nji amal 1-nji amal bilen meşgullanýan A monitoryna girmeli. 1-nji geleşik bilen baglanyşykly uly meseleleriň biri, synagda tapmak aňsat däl. Mysalda beýan edilen ýagdaýynda-da sapaklar petiklenip bilmez, ýagny bu ýagdaý yzygiderli köpelmez, bu diagnostikany ep-esli çylşyrymlaşdyrýar. Umuman aýdanyňda, kesgitleme bermezlik meselesi köp okamak üçin mahsusdyr (muňa garamazdan muny aňsatlaşdyrmaýar). Şonuň üçin kod gözden geçirmek, köp sapakly programmalaryň hilini ýokarlandyrmakda möhüm rol oýnaýar, sebäbi synag wagtynda köpeltmek kyn bolan ýalňyşlyklary kesgitlemäge mümkinçilik berýär. Bu, elbetde, programmanyň synagdan geçirilmeginiň zerurlygyny aňlatmaýar, diňe kod gözden geçirmegi ýatdan çykarmaly däldiris. Bu koduň petiklenmeginiň öňüni almak üçin näme etmeli? Bu blokirleme, hasaby sinhronlaşdyrmagyň başga tertipde bolup biljekdigi bilen ýüze çykýar. Şoňa görä, hasaplarda haýsydyr bir sargyt girizseňiz (bu, A hasabynyň B hasabyndan azdygyny aýtmaga mümkinçilik berýän käbir düzgün), bu mesele aradan aýrylar. Muny nädip etmeli? Birinjiden, hasaplarda san, kiçi harp ýa-da tebigy tertip düşünjesi bilen özboluşly kesgitleýji (mysal üçin, hasap belgisi) bar bolsa (setirleri leksikografiki tertipde deňeşdirip bolar, şonda özümizi bagtly hasaplap bileris we ederis elmydama ilki has kiçi hasabyň monitoryny, soň bolsa has uly hasabyny (ýa-da tersine) alyp bileris.
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)}
			}
		}
	}
}
Ikinji wariant, eger şeýle kesgitleýjimiz ýok bolsa, özümiz tapmaly bolarys. Ilkinji takmynan, obýektleri hash kody bilen deňeşdirip bileris. Başgaça bolarlar. Theyöne şol bir zat bolup çyksa näme etmeli? Soňra sinhronizasiýa üçin başga bir obýekt goşmaly bolarsyňyz. Biraz çylşyrymly bolup görünmegi mümkin, ýöne näme edip bilersiňiz? Mundan başga-da, üçünji obýekt gaty seýrek ulanylar. Netije şeýle bolar:
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)
				}
			}
		}
	}
}

Obýektleriň arasyndaky petik

Beýan edilen blokirleme şertleri, diagnoz goýmagyň iň aňsat ýagdaýyny görkezýär. Köplenç köp sapakly programmalarda dürli obýektler şol bir sinhron bloklara girmäge synanyşýarlar. Munuň petiklenmegine sebäp bolup biler. Aşakdaky mysaly gözden geçiriň: uçuş dispetçeri programmasy. Uçarlar, barmaly ýerine ýetenlerinde gözegçä habar berýärler we gonmaga rugsat soraýarlar. Gözegçi, uçýan uçar hakda ähli maglumatlary öz ugrunda saklaýar we kartadaky ýerlerini meýilleşdirip bilýär.
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;
	}
}
Bu kodda gulplama sebäp bolup biljek bir näsazlygyň bardygyna düşünmek, öňküsinden has kyn. Bir seretseň, gaýtadan sinhronlamak ýok, ýöne beýle däl. setLocationSynp Planewe getMapsynp usullarynyň Dispatchersinhronlaşdyrylýandygyny we öz içindäki beýleki synplaryň sinhronlaşdyrylan usullaryny çagyrýandygyny eýýäm gören bolsaňyz gerek . Bu, köplenç erbet amal. Muny nädip düzedip boljakdygyny indiki bölümde ara alyp maslahatlaşarys. Netijede, uçar ýerleşýän ýerine gelse, kimdir biri kartoçka almak kararyna gelen pursadynda petik bolup biler. .Agny, mysallar we degişlilikde mysal monitorlaryny eýelejek usullar getMapwe usullar atlandyrylar . Usul soňra jaň eder (esasanam häzirki iş bilen meşgullanýan mysalda ), her hadysanyň monitorynyň erkin bolmagyna garaşar . Şol bir wagtyň özünde, usul diýlip atlandyrylar , mysal üçin monitor kartany çyzmak bilen meşgullanýar. Netije petik. setLocationDispatcherPlanegetMapplane.getLocationPlanePlanesetLocationdispatcher.requestLandingDispatcher

Çagyryşlary açyň

Öňki bölümde beýan edilişi ýaly ýagdaýlardan gaça durmak üçin köpçülikleýin jaňlary beýleki obýektleriň usullaryna ulanmak maslahat berilýär. .Agny, sinhronlaşdyrylan blokdan başga obýektleriň jaň usullary. Usullar açyk jaňlar ýörelgesini ulanyp täzeden ýazylsa setLocation, getMappetiklemek mümkinçiligi aradan aýrylar. Mysal üçin, şeýle görüner:
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;
}

Resurslaryň petiklenmegi

Bir wagtyň özünde diňe bir sapagyň ulanyp boljak käbir çeşmelerine girmäge synanyşanyňyzda möhletler hem bolup biler. Mysal üçin maglumat bazasyna birikmek howzy bolup biler. Käbir sapaklar bir wagtyň özünde iki birikmä girmeli bolsa we dürli sargytlar bilen girýän bolsa, bu petiklenip biler. Aslynda, bu hili gulplama sinhronizasiýa tertibiniň gulplanmagyndan tapawudy ýok, käbir kodlary ýerine ýetirjek bolanyňyzda däl-de, çeşmelere girmäge synanyşanyňyzda ýüze çykýar.

Gatnaşyklardan nädip saklanmaly?

Elbetde, kod hiç hili ýalňyşsyz ýazylan bolsa (mysallary öňki bölümlerde görüpdik), onda hiç hili petik bolmaz. Emma kodunyň ýalňyşsyz ýazylmagyna kim kepil geçip biler? Elbetde, synag ýalňyşlyklaryň ep-esli bölegini kesgitlemäge kömek edýär, ýöne öň görşümiz ýaly köp sapakly koddaky ýalňyşlyklary anyklamak aňsat däl we synagdan soň hem petikli ýagdaýlaryň ýokdugyna göz ýetirip bilmersiňiz. Nädip-de bolsa petiklemekden gorap bilerismi? Jogap hawa. Şuňa meňzeş usullar köplenç petiklerden gutulmaly maglumatlar bazasynyň hereketlendirijilerinde ulanylýar (maglumatlar bazasyndaky amal mehanizmi bilen baglanyşykly). LockBukjada bar bolan interfeýs we ýerine ýetiriş usullary java.util.concurrent.locks, usuly ulanyp, bu synpyň mysaly bilen baglanyşykly monitory eýelemäge synanyşmaga mümkinçilik berýär tryLock(monitory eýelemek mümkin bolsa hakyky bolýar). Interfeýsi amala aşyrýan jübüt obýektlerimiz bar Lockwe monitorlary özara petiklemezlik üçin ele almalydyrys. Muny şeýle durmuşa geçirip bilersiňiz:
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();
			}
		}
	}
}
Bu programmada görşüňiz ýaly, özara petiklemek mümkinçiligini aradan aýyrmak bilen, iki monitory eýeleýäris. Blokyň try- finallyzerurdygyny ýadyňyzdan çykarmaň, sebäbi paketdäki synplar java.util.concurrent.locksmonitory awtomatiki çykarmaýar we işiňiz ýerine ýetirilende käbir kadadan çykmalar ýüze çyksa, monitor gulplanan ýagdaýda galar. Giriş möhletini nädip anyklamaly? JVM möhletleri gulplama sapaklarynda görkezip, anyklamaga mümkinçilik berýär. Şeýle zibil sapaklaryň haýsy ýagdaýdadygy barada maglumatlary öz içine alýar. Eger petiklenen bolsa, zibil sapagyň çykmagyna garaşýan monitor hakda maglumatlary öz içine alýar. JVM sapaklary taşlamazdan ozal garaşýan (meşgul) monitorlaryň grafigine seredýär we siklleri tapsa, gatnaşyjy monitorlary we sapaklary görkezýän petik maglumatlary goşýar. Gulplanan sapaklaryň bir bölegi şuňa meňzeýär:
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)
Aboveokardaky zibil bazasy bilen işleýän iki sapagyň biri-birini petikländigini açyk görkezýär. Bu JVM aýratynlygyny ulanyp, möhleti anyklamak üçin programmanyň dürli ýerlerinde sapak zyňmak işine jaňlar goýmaly we programmany synagdan geçirmeli. Ondan soň, ýüze çykan surnallary derňemeli. Eger petiklenendigini görkezseler, zibilden alnan maglumatlar onuň ýüze çykan şertlerini anyklamaga kömek eder. Umuman aýdanyňda, petiklenen mysallardaky ýaly ýagdaýlardan gaça durmaly. Şeýle ýagdaýlarda, programma durnukly işlär. Testingöne synag we kod gözden geçirmegi ýatdan çykarmaň. Mesele ýüze çyksa ýüze çykarmaga kömek eder. Gulp meýdançasynyň dikeldilmegi möhüm ähmiýete eýe bolan ulgamy ösdürýän ýagdaýyňyzda, “Gulplardan nädip gaça durmaly?” Bölüminde beýan edilen usuly ulanyp bilersiňiz. Bu ýagdaýda paketdäki lockInterruptiblyinterfeýs usuly hem peýdaly bolup biler . Bu usuly ulanyp, monitory eýelän sapagy kesmäge mümkinçilik berýär (şeýlelik bilen monitory boşadyň). Lockjava.util.concurrent.locks
Teswirler
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION