JavaRush /Java блогу /Random-KY /Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуру...
Viacheslav
Деңгээл

Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу

Группада жарыяланган

Киришүү

Ошентип, биз Java'да жиптер бар экенин билебиз, алар жөнүндө сиз " Жаваны жип менен буза албайсыз: I бөлүк - Жиптер " сынынан окуй аласыз . Жиптер бир эле учурда ишти аткаруу үчүн керек. Ошондуктан, жиптер кандайдыр бир жол менен бири-бири менен өз ара аракеттениши мүмкүн. Келгиле, бул кантип болуп жатканын жана бизде кандай негизги көзөмөл бар экенин түшүнөлү. Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 1

Түшүм

Thread.yield() ыкмасы сырдуу жана сейрек колдонулат. Интернетте анын сүрөттөлүшүнүн көптөгөн варианттары бар. Айрымдар жиптердин кандайдыр бир кезеги жөнүндө жазышат, анда жип алардын артыкчылыктарын эске алуу менен ылдый жылат. Кимдир бирөө жип өзүнүн статусун иштетилгенден иштетилүүчүгө өзгөртөт деп жазат (бирок бул статустарга эч кандай бөлүнүү жок жана Java алардын ортосунда айырмаланbyte). Бирок, чындыгында, баары алда канча белгисиз жана кандайдыр бир мааниде жөнөкөй. Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 2Метод documentациясынын темасында yieldката бар " JDK-6416721: (өзгөчө жип) Fix Thread.yield() javadoc ". Эгер сиз аны окусаңыз, чындыгында метод yieldJava жип пландоочусуна бул жипти аткарууга азыраак убакыт берorши мүмкүн деген сунушту гана берет экен. Бирок иш жүзүндө эмне болот, пландаштыруучу сунушту угабы жана ал эмне кылаары JVM жана операциялык тутумдун ишке ашырылышынан көз каранды. Же балким башка факторлордон. Бардык башаламандыктар, кыязы, Java тorн иштеп чыгуу учурунда көп теманы кайра карап чыгуу менен шартталган. Көбүрөөк " Java Thread.yield() ге кыскача киришүү " сынынан окуй аласыз .

Уйку - Уктап калуу жип

Жип аны аткаруу учурунда уктап калышы мүмкүн. Бул башка жиптер менен өз ара аракеттенүүнүн эң жөнөкөй түрү. Java виртуалдык машинасы орнотулган, Java codeу аткарылган операциялык тутумда Thread Scheduler деп аталган өзүнүн жип пландоочусу бар. Кайсы жипти качан иштетүүнү ал чечет. Программист бул пландаштыргыч менен түздөн-түз Java codeунан өз ара аракеттене алbyte, бирок ал JVM аркылуу пландоочудан жипти бир азга тындырып, "аны уктатууну" суранышы мүмкүн. Кененирээк " Thread.sleep() " жана " Multithreading кантип иштейт " деген макалалардан окуй аласыз . Мындан тышкары, сиз Windows OS системасында жиптер кантип иштээрин биле аласыз: " Windows Threadдин ичкилери ". Эми муну өз көзүбүз менен көрөбүз. Келгиле, төмөнкү codeду файлга сактайлы HelloWorldApp.java:
class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Waked up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Көрүнүп тургандай, бизде 60 секунд күткөн тапшырма бар, андан кийин программа аяктайт. Биз түзөбүз javac HelloWorldApp.javaжана иштетебиз java HelloWorldApp. Өзүнчө терезеде ишке киргизүү жакшы. Мисалы, Windows'до мындай болот: start java HelloWorldApp. jps буйругун колдонуу менен процесстин PID codeун табабыз жана : аркылуу жиптердин тизмесин ачабыз jvisualvm --openpid pidПроцесса: Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 3Көрүнүп тургандай, биздин жип Уйку абалына кирди. Чындыгында, учурдагы жипти уктоо дагы кооз жасалышы мүмкүн:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Сиз, балким, биз бардык жерде иштеп жатканын байкагандыр InterruptedException? Эмне үчүн экенин түшүнүп алалы.

Жипти үзүү же Thread.interrupt

Кеп нерсе, жип уйкуда күтүп жатканда, кимдир бирөө бул күтүүнү үзгүлтүккө учураткысы келиши мүмкүн. Бул учурда биз мындай өзгөчөлүктү чечебиз. Thread.stopБул ыкма эскирген деп жарыялангандан кийин жасалган , б.а. эскирген жана колдонуу үчүн жагымсыз. Мунун себеби, бул ыкманы чакырганда, stopжип жөн эле "өлтүрүлгөн", бул абдан күтүүсүз болгон. Агым качан токтотуларын биле алган жокпуз, маалыматтардын ырааттуулугуна кепилдик бере алган жокпуз. Сиз файлга маалыматтарды жазып жатканыңызды элестетиңиз, андан кийин агым жок болот. Ошондуктан, алар агымды өлтүрбөй, аны үзгүлтүккө учурашы керек деп кабарлоо логикага ылайыктуураак деп чечишти. Буга кандай реакция кылуу агымдын өзүнөн көз каранды. Кененирээк маалыматты Oracle'дын " Thread.stop эмне үчүн эскирген? " Келгиле, бир мисал карап көрөлү:
public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
Бул мисалда биз 60 секунд күтпөйбүз, бирок ошол замат "Үзгүлтүккө учураган" басып чыгарабыз. Себеби, биз жиптин ыкмасын атадык interrupt. Бул ыкма "үзүлүү статусу деп аталган ички желекти" орнотот. Башкача айтканда, ар бир жип түздөн-түз жеткorктүү эмес ички желеги бар. Бирок бизде бул желек менен иштешүүнүн жергorктүү ыкмалары бар. Бирок бул жалгыз жол эмес. Жип бир нерсени күтпөстөн, жөн гана иш-аракеттерди аткаруу процессинде болушу мүмкүн. Бирок, алар анын ишинин белгилүү бир учурда аны бүтүрүүнү каалай турганын камсыздай алат. Мисалы:
public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			//Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
Жогорудагы мисалда цикл whileсырттан үзгүлтүккө учурамайынча иштей турганын көрө аласыз. isInterrupted желек жөнүндө билүү үчүн маанилүү нерсе , эгерде биз аны кармасак InterruptedException, желек isInterruptedбаштапкы абалга келтирилет, андан кийин isInterruptedал жалган деп кайтарат. Thread классында ошондой эле учурдагы жипке гана тиешелүү статикалык метод бар - Thread.interrupted() , бирок бул ыкма желекти жалганга кайтарат! Кененирээк " Жипти үзүү " бөлүмүнөн окуй аласыз .

Кошулуу — Башка жиптин бүтүшүн күтүп жатабыз

Күтүүнүн эң жөнөкөй түрү - башка жиптин аягына чыгышын күтүү.
public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
Бул мисалда, жаңы жип 5 секунд уктайт. Ошол эле учурда негизги жип уктап жаткан жип ойгонуп, ишин бүткүчө күтөт. JVisualVM аркылуу карасаңыз, жиптин абалы мындай болот: Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 4Мониторинг куралдарынын аркасында жип менен эмне болуп жатканын көрө аласыз. Метод joinабдан жөнөкөй, анткени ал жөн гана java codeу бар ыкма, waitал чакырылган жип тирүү кезинде аткарылат. Жип өлгөндөн кийин (аяктоодо), күтүү токтотулат. Бул ыкманын бүт сыйкыры join. Ошондуктан, келгиле, эң кызыктуу бөлүгүнө өтөбүз.

Монитор концепциясы

Көп агымда Монитор деген нерсе бар. Жалпысынан алганда, монитор деген сөз латын тorнен "көзөмөлчү" же "көзөмөлчү" деп которулат. Бул макаланын алкагында биз маани-маңызын эстеп калууга аракет кылабыз, ал эми каалагандар үчүн мен сизден материалды шилтемелерден чоо-жайы менен таанышууну суранам. Келгиле, саякатыбызды Java тorнин спецификациясынан баштайлы, башкача айтканда JLS: " 17.1. Синхронизация ". Анда төмөндөгүлөр айтылат: Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 5Жиптердин ортосундагы синхрондоштуруу максатында Java "Монитор" деп аталган белгилүү бир механизмди колдонот экен. Ар бир an objectтин аны менен байланышкан монитору бар жана жиптер аны бекитип же кулпусун ача алат. Андан кийин, биз Oracle веб-сайтынан окуу куралын табабыз: “ Ички кулпулар жана синхрондоштуруу ”. Бул окуу куралы Java-да синхрондоштуруу ички кулпу же монитордун кулпусу деп аталган ички an objectтин айланасында курулаарын түшүндүрөт. Көп учурда мындай кулпу жөн гана "монитор" деп аталат. Ошондой эле биз Javaдагы ар бир an objectтин аны менен байланышкан ички кулпу бар экенин дагы бир жолу көрөбүз. Сиз окуй аласыз " Java - Intrinsic Locks and Synchronization ". Андан кийин, Javaдагы an objectтин монитор менен кандай байланышы бар экенин түшүнүү маанилүү. Javaдагы ар бир an objectтин аталышы бар - codeдон программист үчүн жеткorктүү эмес, бирок виртуалдык машина an objectтер менен туура иштеши керек болгон ички метаберorштердин бир түрү. Объекттин аталышы төмөнкүдөй көрүнгөн MarkWord камтыйт: Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

Бул жерде Хабрдын макаласы абдан пайдалуу: " Бирок көп агым кантип иштейт? I бөлүк: синхрондоштуруу ." Бул макалага JDK катакеринин тапшырма блогунун кыскача баяндамасынан сыпаттаманы кошуу керек: “ JDK-8183909 ”. Сиз ошол эле нерсени окуй аласыз " JEP-8183909 ". Ошентип, Java-да монитор an object менен байланышкан жана жип бул жипти бөгөттөп коюшу мүмкүн, же алар "кулпу алам" дешет. Эң жөнөкөй мисал:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Ошентип, ачкыч сөздү колдонуп, synchronizedучурдагы жип (бул code саптары аткарылган) an object менен байланышкан мониторду колдонууга objectжана "кулпу алууга" же "мониторду басып алууга" аракет кылат (экинчи вариант дагы жакшыраак). Монитор үчүн эч кандай талаш жок болсо (б.а. башка эч ким бир эле an objectте синхрондоштурууну каалабаса), Java "бир жактуу кулпу" деп аталган оптималдаштырууну ишке ашырууга аракет кыла алат. Mark Word программасындагы an objectтин аталышы тиешелүү тегди жана монитор кайсы жипке тиркелгендигинин жазуусун камтыйт. Бул мониторду тартууда ашыкча чыгымды азайтат. Монитор мурунтан эле башка жипке байланган болсо, анда бул кулпу жетишсиз. JVM кийинки кулпулоо түрүнө - негизги кулпуга өтөт. Ал салыштыруу жана алмаштыруу (CAS) операцияларын колдонот. Ошол эле учурда, Mark Word'тун аталышы мындан ары Mark Wordдун өзүн сактаbyte, бирок анын сактагычына шилтеме + теги JVM биз негизги кулпулоону колдонуп жатканыбызды түшүнүшү үчүн өзгөртүлгөн. Эгерде бир нече жиптердин мониторунда талаш-тартыш болсо (бири мониторду басып алды, экинчиси монитордун чыгышын күтүп жатат), анда Mark Word программасындагы теги өзгөрүп, Марк Word мониторго шилтеме катары сактай баштайт. an object - JVMдин кандайдыр бир ички an objectи. JEPде айтылгандай, бул учурда бул an objectти сактоо үчүн Native Heap эстутум аймагында орун талап кылынат. Бул ички an objectтин сактагычына шилтеме Mark Word an objectинде жайгашат. Ошентип, биз көрүп тургандай, монитор чындыгында жалпы ресурстарга бир нече жиптердин жеткorктүүлүгүн синхрондоштурууну камсыз кылуучу механизм болуп саналат. JVM которуштуруучу бул механизмдин бир нече ишке ашырылышы бар. Ошондуктан, жөнөкөйлүк үчүн, монитор жөнүндө сөз болгондо, биз кулпулар жөнүндө сөз болуп жатат. Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 7

Шайкештирилген жана кулпу менен күтүүдө

Монитор түшүнүгү, биз мурда көргөндөй, "синхрондоштуруу блогу" түшүнүгү менен тыгыз байланышта (же ал ошондой эле критикалык бөлүм деп аталат). Келгиле, бир мисал карап көрөлү:
public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized (lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized (lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print("  " + i);
		}
		System.out.println(" ...");
	}
}
Бул жерде негизги жип алгач тапшырманы жаңы жипке жөнөтөт, андан кийин дароо кулпуну “басып алат” жана аны менен узак операцияны аткарат (8 секунд). Бул убакыттын ичинде, тапшырма анын аткарылышы үчүн блокко кире алbyte synchronized, анткени кулпу мурунтан эле ээлеген. Эгерде жип кулпу ала албаса, ал аны монитордон күтөт. Аны алаары менен аткарууну улантат. Жип монитордон чыгып кеткенде, ал кулпусун бошотот. JVisualVMде бул мындай болот: Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 8Көрүнүп тургандай, JVisualVMдеги статус "Монитор" деп аталат, анткени жип бөгөттөлгөн жана мониторду ээлей алbyte. Сиз ошондой эле codeдон жиптин абалын биле аласыз, бирок бул мамлекеттин аталышы JVisualVM терминдерине дал келбейт, бирок алар окшош. Бул учурда, th1.getState()цикл BLOCKEDfor кайтарып берет , анткени Цикл иштеп жатканда, мониторду жип ээлейт , ал эми жип бөгөттөлүп, кулпу кайтарылганга чейин ишин уланта алbyte. Синхрондоштуруу блокторунан тышкары, бүтүндөй ыкманы синхрондоштурууга болот. Мисалы, класстын ыкмасы : lockmainth1HashTable
public synchronized int size() {
	return count;
}
Убакыттын бир бирдигинде бул ыкма бир гана жип менен аткарылат. Бирок бизге кулпу керек, туурабы? Ооба мага керек. Объекттик методдордо кулпу болот this. Бул темада кызыктуу талкуу жүрүп жатат: " Синхрондоштурулган блоктун ордуна Синхрондоштурулган методду колдонуунун артыкчылыгы барбы? ". Эгерде метод статикалык болсо, анда кулпу болбойт this(анткени статикалык ыкма үчүн болушу мүмкүн эмес this), класс an objectи (Мисалы, Integer.class).

Күтүү жана монитордо күтүү. кабарлоо жана кабарлоо Бардык ыкмалар

Thread мониторго туташтырылган дагы бир күтүү ыкмасына ээ. sleepжанадан айырмаланып join, аны жөн эле чакырууга болбойт. Жана анын аты wait. Метод waitбиз мониторун күткүбүз келген an objectте аткарылат. Келгиле, бир мисал карап көрөлү:
public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // task будет ждать, пока его не оповестят через lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // После оповещения нас мы будем ждать, пока сможем взять лок
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // Ждём и после этого забираем себе лок, оповещаем и отдаём лок
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVMде ал төмөнкүдөй болот: Бул кандайча иштээрин түшүнүү үчүн, ыкмаларга шилтеме жасоону Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 10унутпаңыз . Жип менен байланышкан ыкмалар . Бирок бул жерде жооп бар. Эсибизде болгондой, Javaдагы ар бир an objectтин аталышы бар. Тема ар кандай кызматтык маалыматты, анын ичинде монитор жөнүндө маалыматты — кулпулоо абалы жөнүндө маалыматтарды камтыйт. Жана биз эсибизде тургандай, ар бир an objectтин (б.а. ар бир инстанциянын) ички кулпу деп аталган ички JVM an objectи менен байланышы бар, ал дагы монитор деп аталат. Жогорудагы мисалда, тапшырма биз менен байланышкан монитордогу синхрондоштуруу блогуна кирээрибизди сүрөттөйт . Бул монитордун кулпусун алуу мүмкүн болсо, анда . Бул тапшырманы аткарган жип мониторду бошотот , бирок монитордо эскертүүнү күткөн жиптердин кезегине кошулат . Жиптердин бул кезеги WAIT-SET деп аталат, ал маңызын туурараак чагылдырат. Бул кезекке караганда бир топ. Жип тапшырма тапшырмасы менен жаңы жипти түзөт, аны баштайт жана 3 секунд күтөт. Бул жогорку ыктымалдуулук менен жаңы жипти жиптин алдында кулпусун кармап , монитордо кезекке турууга мүмкүндүк берет. Андан кийин жип өзү синхрондоштуруу блогуна кирет жана монитордо жип жөнүндө кабарлоону ишке ашырат. Кабарлоо жөнөтүлгөндөн кийин жип мониторду бошотот жана жаңы жип (мурда күтүп турган) монитордун чыгышын күткөндөн кийин аткарууну улантат. Билдирүүнү жиптердин бирине гана ( ) же кезектеги бардык жиптерге ( ) жөнөтүүгө болот . " Java'дагы notify() менен notifyAll() ортосундагы айырма " бөлүмүндө кененирээк окуй аласыз . Бул кабарлоо тартиби JVM ишке ашыруу көз каранды экенин белгилей кетүү маанилүү. Кененирээк " Ачкачылыкты кантип чечсе болот notify and notifyall? ". Синхрондоштуруу an objectти көрсөтпөстөн жүргүзүлүшү мүмкүн. Бул codeдун өзүнчө бөлүмү эмес, бүтүндөй ыкма менен шайкештештирилгенде жасалышы мүмкүн. Мисалы, статикалык методдор үчүн кулпу класс an objectи болот ( аркылуу алынган ): waitnotifyjava.lang.ObjectObjectlockwaitlocklockmainmainmainlockmainlocklocknotifynotifyAll.class
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
Кулпуларды колдонуу жагынан эки ыкма тең бирдей. Эгерде метод статикалык болбосо, анда синхрондоштуруу агымга instance, башкача айтканда, ылайык ишке ашырылат this. Баса, мурда биз ыкманы колдонуу менен getStateжиптин статусун алууга болот деп айтканбыз. Ошентип, бул жерде монитор кезекте турган жип, эгер ыкма waitкүтүү убактысынын чегин көрсөтсө, абал WAITING же TIMED_WAITING болот. Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 11

Жиптин жашоо цикли

Көрүнүп тургандай, агым жашоонун жүрүшүндө өзүнүн статусун өзгөртөт. Негизи бул өзгөрүүлөр жиптин жашоо цикли болуп саналат. Жип жаңы гана түзүлгөндө, ал ЖАҢЫ статуска ээ болот. Бул абалда, ал али баштала элек жана Java Thread Scheduler жаңы жип жөнүндө азырынча эч нерсе билбейт. Жип пландоочусу жип жөнүндө бorши үчүн, сиз thread.start(). Андан кийин жип RUNNABLE абалына өтөт. Интернетте Runnable жана Running мамлекеттери бөлүнгөн көптөгөн туура эмес схемалар бар. Бирок бул ката, анткени... Java "чаркоого даяр" жана "иштеп жаткан" статустарын айырмалай алbyte. Жип жандуу, бирок жигердүү эмес (Runnable эмес) болгондо, ал эки абалдын биринде болот:
  • BLOCKED - корголгон бөлүмгө кирүүнү күтөт, б.а. блокко synchonized.
  • КҮТҮҮ – шарттын негизинде башка жипти күтөт. Эгер шарт чын болсо, жип пландоочу жипти баштайт.
Эгерде жип убакыт боюнча күтүп жатса, ал TIMED_WAITING абалында. Эгерде жип иштебей калса (ийгorктүү бүткөрүлгөн же өзгөчө учур менен), ал ТОКТОТУЛГАН статусуна өтөт. Жиптин абалын (анын абалын) билүү үчүн ыкма колдонулат getState. isAliveЖиптерде ошондой эле , эгер жип токтотулбаса, чындыкты кайтаруучу метод бар .

LockSupport жана жип паркинг

Java 1.6дан бери LockSupport деп аталган кызыктуу механизм бар . Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 12Бул класс "уруксат" же уруксатты аны колдонгон ар бир жип менен байланыштырат. parkЧакыруу учурунда ошол эле уруксатты ээлеген уруксат бар болсо, ыкма чалуу дароо кайтып келет. Болбосо бөгөттөлгөн. Методду чалуу, unparkал мурунтан эле жок болсо, уруксатты жеткorктүү кылат. 1 гана уруксат бар. Java API'де LockSupportбелгилүү Semaphore. Келгиле, жөнөкөй мисалды карап көрөлү:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Просим разрешение и ждём, пока не получим его
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
Бул code түбөлүккө күтөт, анткени семафордун азыр 0 уруксаты бар. Ал эми codeду чакырганда acquire(б.а., уруксат сураганда), жип уруксат алганга чейин күтөт. Биз күтүп жаткандыктан, биз аны иштетүүгө милдеттүүбүз InterruptedException. Кызыктуусу, семафор өзүнчө жип абалын ишке ашырат. JVisualVMде карасак, биздин мамлекет күтүү эмес, Парк экенин көрөбүз. Javaны жип менен буза албайсыз: II бөлүк - синхрондоштуруу - 13Дагы бир мисалды карап көрөлү:
public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            //Запаркуем текущий поток
            System.err.println("Will be Parked");
            LockSupport.park();
            // Как только нас распаркуют - начнём действовать
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());

        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
Жиптин абалы КҮТҮП турат, бирок JVisualVM waitдан synchronizedжана parkандан айырмалайт LockSupport. Бул эмне үчүн мынчалык маанилүү LockSupport? Келгиле, кайра Java API'ге кайрылып, Thread State WAITING карап көрөлү . Көрүнүп тургандай, ага кирүүнүн үч гана жолу бар. 2 жол - бул waitжана join. Ал эми үчүнчүсү LockSupport. Java'дагы кулпулар ошол эле принциптерге негизделген LockSupportжана жогорку деңгээлдеги куралдарды билдирет. Келгиле, бирин колдонууга аракет кылалы. Мисалы, карап көрөлү ReentrantLock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
Мурунку мисалдардагыдай эле, бул жерде баары жөнөкөй. lockкимдир бирөө ресурсту чыгарышын күтөт. mainJVisualVMде карасак, жаңы жип жип ага кулпу бергенге чейин токтоп турганын көрөбүз . Кулпулар тууралуу кененирээк бул жерден окуй аласыз: " Java 8де көп агымдуу программалоо. Экинчи бөлүк. Өзгөрүүчү an objectтерге кирүү мүмкүнчүлүгүн синхрондоо " жана " Java Lock API. Теория жана колдонуу мисалы ." Кулпуларды ишке ашырууну жакшыраак түшүнүү үчүн, " Phaser Class " серепинен Phazer жөнүндө окуу пайдалуу . Жана ар кандай синхронизаторлор жөнүндө сөз кылып жатып, сиз Habré боюнча макаланы окушуңуз керек “ Java.util.concurrent.* Синхронизаторлор маалымдамасы ”.

Бардыгы

Бул карап чыгууда биз жиптердин Javaда өз ара аракеттенүү жолдорун карап чыктык. Кошумча материал: #Вячеслав
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION