JavaRush /Блоги Java /Random-TG /Бунбаст дар Java ва усулҳои мубориза бо он
articles
Сатҳи

Бунбаст дар Java ва усулҳои мубориза бо он

Дар гурӯҳ нашр шудааст
Ҳангоми таҳияи замимаҳои бисёр ришта аксар вақт як дилемма ба миён меояд: муҳимтар аз ҳама эътимоднокӣ ё иҷрои барнома аст. Масалан, мо синхронизатсияро барои бехатарии ришта истифода мебарем ва дар ҳолатҳое, ки тартиби ҳамоҳангсозӣ нодуруст аст, мо метавонем ба бунбаст оварда расонем. Мо инчунин ҳавзҳои ришта ва семафорҳоро барои маҳдуд кардани истеъмоли захираҳо истифода мебарем ва иштибоҳ дар ин тарҳ метавонад аз сабаби набудани захираҳо ба бунбаст оварда расонад. Дар ин мақола мо дар бораи он, ки чӣ гуна аз бунбаст пешгирӣ кардан мумкин аст ва инчунин мушкилоти дигар дар иҷрои барнома сӯҳбат хоҳем кард. Мо инчунин дида мебароем, ки чӣ гуна аризаро тавре навиштан мумкин аст, то дар ҳолатҳои басташавӣ барқарор карда шавад. Бунбаст дар Java ва усулҳои мубориза бо он - 1Бунбаст вазъиятест, ки дар он ду ё зиёда равандҳое, ки баъзе захираҳоро ишғол мекунанд, кӯшиш мекунанд, ки баъзе захираҳои дигареро, ки равандҳои дигар ишғол мекунанд, ба даст оранд ва ҳеҷ яке аз равандҳо захираи заруриро ишғол карда наметавонанд ва мувофиқан захираи ишғолшударо озод мекунанд. Ин таъриф хеле умумӣ аст ва аз ин рӯ фаҳмидан душвор аст; Барои фаҳмиши беҳтар, мо бо истифода аз мисолҳо намудҳои бунбастҳоро дида мебароем.

Ҳамоҳангсозӣ Тартиби бастани мутақобила

Вазифаи зеринро дида бароед: ба шумо лозим аст, ки усулеро нависед, ки амалиёти интиқоли миқдори муайяни пулро аз як суратҳисоб ба суратҳисоби дигар анҷом медиҳад. Ҳалли ин метавонад чунин бошад:
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);
			}
		}
	}
}
Дар назари аввал, ин code ба таври муқаррарӣ ҳамоҳанг карда мешавад; мо амалиёти атомии тафтиш ва тағир додани ҳолати ҳисоби манбаъ ва тағир додани ҳисоби таъинот дорем. Бо вуҷуди ин, бо ин стратегияи ҳамоҳангсозӣ, метавонад вазъияти бунбаст ба амал ояд. Биёед мисолеро бубинем, ки ин чӣ гуна рӯй медиҳад. Ду амалиётро анҷом додан лозим аст: аз суратҳисоби А ба суратҳисоби В интиқол х пул ва аз суратҳисоби В ба суратҳисоби А. Аксар вақт ин вазъият ба бунбаст оварда намерасонад, аммо дар як маҷмӯи нохушоянд, транзаксияи 1 монитори суратҳисоби Аро ишғол мекунад, транзаксияи 2 монитори суратҳисоби Bро ишғол мекунад. Дар натиҷа бунбаст ба вуҷуд меояд: транзаксияи 1 мунтазири амалиёти 2 аст, ки монитори ҳисобро озод кунад. B, аммо муомилоти 2 бояд ба монитори А, ки бо транзаксияи 1 ҷойгир аст, дастрасӣ пайдо кунад. Яке аз мушкилоти калон бо бунбастҳо дар он аст, ки онҳо дар санҷиш осон нестанд. Ҳатто дар вазъияте, ки дар мисол оварда шудааст, риштаҳо метавонанд баста нашаванд, яъне ин вазъ пайваста такрор намешавад, ки ташхисро ба таври назаррас мушкил мекунад. Умуман, масъалаи тавсифшудаи ғайридетерминизм барои бисёр ришта хос аст (гарчанде ки ин корро осон намекунад). Аз ин рӯ, баррасии codeҳо дар баланд бардоштани сифати барномаҳои бисёрсоҳавӣ нақши муҳим мебозад, зеро он ба шумо имкон медиҳад, ки хатогиҳоеро, ки ҳангоми санҷиш дубора тавлид кардан душвор аст, муайян кунед. Ин, албатта, маънои онро надорад, ки барнома санҷидан лозим нест; мо набояд танҳо дар бораи баррасии code фаромӯш кунем. Чӣ бояд кард, то ин codeро ба бунбаст оварда нарасонад? Ин басташавӣ аз он сабаб аст, ки ҳамоҳангсозии ҳисоб метавонад бо тартиби дигар сурат гирад. Мувофиқи он, агар шумо дар суратҳисобҳо ягон тартибот ҷорӣ кунед (ин баъзе қоидаҳоест, ки ба шумо имкон медиҳад бигӯед, ки ҳисоби А аз ҳисоби 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)}
			}
		}
	}
}
Варианти дуюм, агар мо чунин идентификатор надошта бошем, мо бояд худамон онро пайдо кунем. Мо метавонем, ба тахмини аввал, an objectҳоро бо codeи hash муқоиса кунем. Эҳтимол, онҳо гуногун хоҳанд буд. Аммо чӣ мешавад, агар онҳо якхела шаванд? Пас шумо бояд an objectи дигарро барои ҳамоҳангсозӣ илова кунед. Он метавонад каме мураккабтар бошад, аммо шумо чӣ кор карда метавонед? Ва гайр аз ин, an objectи сеюм хеле кам истифода мешавад. Натиҷа чунин хоҳад буд:
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)
				}
			}
		}
	}
}

Сарбаста дар байни an objectҳо

Шароити бастани тавсифшуда осонтарин ҳолати бунбаст барои ташхис мебошад. Аксар вақт дар замимаҳои бисёр ришта an objectҳои гуногун кӯшиш мекунанд, ки ба ҳамон блокҳои ҳамоҳангшуда дастрасӣ пайдо кунанд. Ин метавонад боиси басташавӣ гардад. Мисоли зеринро дида бароед: барномаи диспетчери парвоз. Ҳавопаймоҳо ба контроллер ҳангоми расидан ба ҷои таъиншуда хабар медиҳанд ва барои фуруд иҷозат талаб мекунанд. Контроллер тамоми маълумотро дар бораи ҳавопаймоҳое, ки ба самти ӯ парвоз мекунанд, нигоҳ медорад ва метавонад мавқеи онҳоро дар харита тасвир кунад.
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;
	}
}
Фаҳмидани он, ки дар ин code хатое вуҷуд дорад, ки метавонад ба бунбаст оварда расонад, нисбат ба codeи қаблӣ мушкилтар аст. Дар назари аввал, он аз нав ҳамоҳангсозӣ надорад, аммо ин тавр нест. Эҳтимол шумо аллакай пай бурдаед, ки усулҳои setLocationсинф Planeва getMapсинф Dispatcherҳамоҳанг карда шудаанд ва усулҳои ҳамоҳангшудаи синфҳои дигарро дар дохor худ даъват мекунанд. Ин одатан таҷрибаи бад аст. Чӣ тавр онро ислоҳ кардан мумкин аст, дар боби оянда муҳокима карда мешавад. Дар натиҷа, агар ҳавопаймо ба макон расад, лаҳзае, ки касе тасмим гирифт, ки кортро гирад, бунбаст ба амал меояд. Яъне getMapва усулҳо даъват карда мешаванд, ки мутаносибан setLocationмониторҳои инстансияро ишғол мекунанд . Сипас усул занг мезанад (махсусан дар инстансе , ки айни замон банд аст) ва интизор мешавад, ки монитор барои ҳар як инстансия озод шавад . Ҳамзамон, усул даъват карда мешавад , дар ҳоле ки монитор инстансия бо кашидани харита банд аст. Натичаи ин бунбаст аст. DispatcherPlanegetMapplane.getLocationPlanePlanesetLocationdispatcher.requestLandingDispatcher

Зангҳоро кушоед

Барои роҳ надодан ба ҳолатҳое, ки дар боби қаблӣ тавсиф шудааст, тавсия дода мешавад, ки зангҳои оммавӣ ба усулҳои an objectҳои дигар истифода шаванд. Ин аст, ки усулҳои дигар an objectҳои берун аз блоки ҳамоҳангшуда занг занед. Агар усулҳо бо истифода аз принсипи зангҳои кушод аз нав навишта шаванд 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;
}

Бунбасти захираҳо

Бунбастҳо инчунин ҳангоми кӯшиши дастрасӣ ба баъзе захираҳое, ки дар як вақт танҳо як ришта истифода бурда метавонанд, ба амал омада метавонанд. Намунаи он ҳавзи пайвасти пойгоҳи додаҳо хоҳад буд. Агар баъзе риштаҳо бояд дар як вақт ба ду пайвастшавӣ дастрасӣ дошта бошанд ва онҳо бо тартиби гуногун ба онҳо дастрасӣ пайдо кунанд, ин метавонад ба бунбаст оварда расонад. Аслан, ин намуди қулфкунӣ аз қулфкунии тартиби синхронизатсия фарқ надорад, ба истиснои он ки он на ҳангоми кӯшиши иҷрои ягон code, балки ҳангоми кӯшиши дастрасӣ ба захираҳо ба амал меояд.

Чӣ тавр аз бунбаст пешгирӣ кардан мумкин аст?

Албатта, агар code бидуни хато навишта шавад (мисолҳои онҳоро мо дар қисматҳои қаблӣ дидем), пас дар он ҳеҷ гуна бунбаст нахоҳад буд. Аммо кй кафолат дода метавонад, ки codeи у бе хато навишта мешавад? Албатта, санҷиш барои муайян кардани қисми муҳими хатогиҳо кӯмак мекунад, аммо тавре ки мо қаблан дидем, хатогиҳо дар codeи бисёр риштаро ташхис кардан осон нест ва ҳатто пас аз санҷиш шумо боварӣ ҳосил карда наметавонед, ки ягон ҳолати сарбаста вуҷуд надорад. Оё мо метавонем худро бо ягон роҳ аз блок муҳофизат кунем? Ҷавоб ҳа аст. Усулҳои шабеҳ дар муҳаррикҳои пойгоҳи додаҳо истифода мешаванд, ки аксар вақт бояд аз бунбаст барқарор шаванд (бо механизми транзаксия дар пойгоҳи додаҳо алоқаманд аст). Интерфейс Lockва татбиқи он, ки дар баста мавҷуд аст, java.util.concurrent.locksба шумо имкон медиҳад, ки монитореро, ки бо намунаи ин синф алоқаманд аст, бо истифода аз усул ишғол кунед tryLock(агар имкони ишғол кардани монитор имконпазир бошад, ҳақиқиро бармегардонад). Фарз мекунем, ки мо як ҷуфт an object дорем, ки интерфейсро амалӣ мекунанд 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, зарур аст, ки зангҳоро ба амалиёти партови ришта дар ҷойҳои гуногуни барнома ҷойгир кунед ва барномаро санҷед. Баъдан, шумо бояд гузоришҳои натиҷашударо таҳлил кунед. Агар онҳо нишон диҳанд, ки бунбаст ба амал омадааст, маълумот аз партовгоҳ барои муайян кардани шароите, ки дар он рух додааст, кӯмак хоҳад кард. Умуман, шумо бояд аз ҳолатҳое, ки дар мисолҳои сарбастаанд, канорагирӣ кунед. Дар чунин ҳолатҳо, барнома эҳтимолан устувор кор мекунад. Аммо дар бораи санҷиш ва баррасии code фаромӯш накунед. Ин барои муайян кардани мушкилот дар сурати пайдо шудани онҳо кӯмак мекунад. Дар ҳолатҳое, ки шумо системаеро таҳия карда истодаед, ки барои он барқарорсозии майдони бунбаст муҳим аст, шумо метавонед усули дар фасли «Чӣ гуна аз бунбаст пешгирӣ кардан мумкин аст?» тавсифшударо истифода баред. Дар ин ҳолат, усули lockInterruptiblyинтерфейс Lockаз java.util.concurrent.locks. Он ба шумо имкон медиҳад, ки бо ин усул риштаеро, ки мониторро ишғол кардааст, қатъ кунед (ва аз ин рӯ мониторро озод кунед).
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION