CodeGym /Java блог /Случаен /По-добре заедно: Java и клас Thread. Част II — Синхрониза...
John Squirrels
Ниво
San Francisco

По-добре заедно: Java и клас Thread. Част II — Синхронизация

Публикувано в групата

Въведение

И така, знаем, че Java има нишки. Можете да прочетете за това в ревюто, озаглавено По-добре заедно: Java и класът Thread. Част I — Нишки на изпълнение . Нишките са необходими за паралелно извършване на работа. Това прави много вероятно нишките по няHowъв начин да взаимодействат една с друга. Нека да разгледаме How се случва това и Howви основни инструменти имаме. По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 1

Доходност

Thread.yield() е объркващо и се използва рядко. В интернет е описан по много различни начини. Включително някои хора пишат, че има няHowва опашка от нишки, в която нишката ще се спусне въз основа на приоритетите на нишката. Други хора пишат, че една нишка ще промени статуса си от "Running" на "Runnable" (въпреки че няма разлика между тези статуси, т.е. Java не прави разлика между тях). Реалността е, че всичко това е много по-малко известно и все пак по-просто в известен смисъл. По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 2Има регистрирана грешка ( JDK-6416721: (spec thread) Fix Thread.yield() javadoc ) за yield()documentацията на метода. Ако го прочетете, става ясно, чеyield()всъщност само предоставя някои препоръки към програмата за планиране на нишки на Java, че тази нишка може да получи по-малко време за изпълнение. Но Howво всъщност се случва, т.е. дали планировчикът действа по препоръката и Howво прави като цяло, зависи от внедряването на JVM и операционната система. И може да зависи и от някои други фактори. Цялото объркване най-вероятно се дължи на факта, че многонишковостта е преосмислена с развитието на езика Java. Прочетете повече в прегледа тук: Кратко въведение в Java Thread.yield() .

сън

Нишката може да заспи по време на нейното изпълнение. Това е най-лесният тип взаимодействие с други нишки. Операционната система, която изпълнява Java виртуалната машина, която изпълнява нашия Java code, има собствен планировчик на нишки . Той решава коя нишка да започне и кога. Програмистът не може да взаимодейства с този планировчик директно от Java code, а само чрез JVM. Той or тя може да поиска от планировчика да спре нишката за известно време, т.е. да я постави в режим на заспиване. Можете да прочетете повече в тези статии: Thread.sleep() и Как работи Multithreading . Можете също така да проверите How работят нишките в операционни системи Windows: Вътрешни елементи на 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("Woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Както можете да видите, имаме няHowва задача, която чака 60 секунди, след което програмата приключва. Компorраме с помощта на командата " javac HelloWorldApp.java" и след това стартираме програмата с помощта на " java HelloWorldApp". Най-добре е да стартирате програмата в отделен прозорец. Например в Windows е така: start java HelloWorldApp. Използваме командата jps, за да получим PID (ИД на процеса) и отваряме списъка с нишки с " jvisualvm --openpid pid: По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 3Както можете да видите, нишката ни вече има статус "Спящ". Всъщност има по-елегантен начин да помогнем нишката ни има сладки сънища:

try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Woke up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Забелязахте ли, че се справяме InterruptedExceptionнавсякъде? Нека разберем защо.

Thread.interrupt()

Работата е там, че докато дадена нишка чака/спи, някой може да поиска да я прекъсне. В този случай ние обработваме InterruptedException. Този механизъм е създаден, след като Thread.stop()методът е обявен за Deprecated, т.е. остарял и нежелан. Причината беше, че когато stop()методът беше извикан, нишката беше просто "убита", което беше много непредвидимо. Не можехме да знаем кога нишката ще бъде спряна и не можехме да гарантираме съгласуваност на данните. Представете си, че записвате данни във файл, докато нишката е убита. Вместо да убият нишката, създателите на Java решиха, че би било по-логично да й кажат, че трябва да бъде прекъсната. Как да се отговори на тази информация е въпрос на самата тема. За повече подробности прочетете Защо Thread.stop е остарял?на уебсайта на Oracle. Да разгледаме един пример:

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()метода в нишката. Този метод задава вътрешен флаг, наречен "състояние на прекъсване". Тоест всяка нишка има вътрешен флаг, който не е директно достъпен. Но имаме собствени методи за взаимодействие с този флаг. Но това не е единственият начин. Една нишка може да работи, да не чака нещо, просто да извършва действия. Но може да предвиди, че други ще искат да прекратят работата му в определено време. Например:

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()ще върне false. Класът Thread също има статичен метод Thread.interrupted() , който се прилага само към текущата нишка, но този метод нулира флага на false! Прочетете повече в тази глава, озаглавена Прекъсване на нишка .

Присъединете се (Изчакайте друга нишка да приключи)

Най-простият тип изчакване е изчакването да завърши друга нишка.

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 и клас Thread.  Част II — Синхронизация - 4Благодарение на инструментите за наблюдение можете да видите Howво се случва с нишката. Методът joinе доста прост, защото е просто метод с Java code, който се изпълнява, wait()докато нишката, на която е извикан, е жива. Веднага щом нишката умре (когато приключи с работата си), чакането се прекъсва. И това е цялата магия на join()метода. И така, нека да преминем към най-интересното.

Монитор

Многонишковостта включва концепцията за монитор. Думата монитор идва на английски чрез латински от 16-ти век и означава „инструмент or устройство, използвано за наблюдение, проверка or поддържане на непрекъснат запис на процес“. В контекста на тази статия ще се опитаме да покрием основите. За всеки, който иска подробности, моля, потопете се в свързаните материали. Започваме нашето пътуване със спецификацията на езика Java (JLS): 17.1. Синхронизиране . Там се казва следното: По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 5Оказва се, че Java използва "монитор" механизъм за синхронизация между нишките. Монитор е свързан с всеки обект и нишките могат да го придобият с lock()or да го освободят с unlock(). След това ще намерим урока на уебсайта на Oracle: Вътрешни заключвания и синхронизация. Този урок казва, че синхронизацията на Java е изградена около вътрешен обект , наречен вътрешен заключване or заключване на монитора . Тази ключалка често се нарича просто " монитор ". Също така отново виждаме, че всеки обект в Java има свързано с него вътрешно заключване. Можете да прочетете Java - Вътрешни заключвания и синхронизация . След това ще бъде важно да разберем How обект в Java може да бъде свързан с монитор. В Java всеки обект има заглавка, която съхранява вътрешни метаданни, които не са достъпни за програмиста от codeа, но които са необходими на виртуалната машина, за да работи правилно с обектите. Заглавието на обекта включва "маркираща дума", която изглежда така: По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 6

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

Ето една статия от JavaWorld, която е много полезна: Как виртуалната машина на Java извършва синхронизиране на нишки . Тази статия трябва да се комбинира с описанието от раздела „Резюме“ на следния проблем в системата за проследяване на грешки JDK: JDK-8183909 . Можете да прочетете същото тук: JEP-8183909 . И така, в Java мониторът е свързан с обект и се използва за блокиране на нишка, когато нишката се опита да придобие (or получи) ключалката. Ето най-простият пример:

public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Тук текущата нишка (тази, на която се изпълняват тези редове code) използва ключовата synchronizedдума, за да се опита да използва монитора, свързан сobject"\променлива за получаване/получаване на заключването. Ако никой друг не се бори за монитора (т.е. никой друг не изпълнява синхронизиран code, използвайки същия обект), тогава Java може да се опита да извърши оптимизация, наречена "предубедено заключване". Съответен етикет и запис за това коя нишка притежава заключването на монитора се добавят към думата за маркиране в заглавката на обекта. Това намалява разходите, необходими за заключване на монитор. Ако мониторът преди това е бил собственост на друга нишка, тогава такова заключване не е достатъчно. JVM превключва към следващия тип заключване: "основно заключване". Той използва операции за сравнение и размяна (CAS). Нещо повече, самата маркираща дума на заглавката на обекта вече не съхранява маркираната дума, а по-скоро препратка към мястото, където се съхранява, и етикетът се променя, така че JVM да разбере, че използваме основно заключване. Ако множество нишки се конкурират (конкурират) за монитор (една е придобила заключването, а втора чака заключването да бъде освободено), тогава етикетът в маркиращата дума се променя и маркиращата дума сега съхранява препратка към монитора като обект — няHowва вътрешна единица на JVM. Както е посочено в предложението за подобряване на JDK (JEP), тази ситуация изисква място в областта на Native Heap на паметта, за да се съхрани този обект. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. и секунда чака заключването да бъде освободено), след това етикетът в маркиращата дума се променя и маркиращата дума сега съхранява препратка към монитора като обект — няHowъв вътрешен обект на JVM. Както е посочено в предложението за подобряване на JDK (JEP), тази ситуация изисква място в областта на Native Heap на паметта, за да се съхрани този обект. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. и секунда чака заключването да бъде освободено), след това етикетът в маркиращата дума се променя и маркиращата дума сега съхранява препратка към монитора като обект — няHowъв вътрешен обект на JVM. Както е посочено в предложението за подобряване на JDK (JEP), тази ситуация изисква място в областта на Native Heap на паметта, за да се съхрани този обект. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. и думата за маркиране сега съхранява препратка към монитора като обект - няHowъв вътрешен обект на JVM. Както е посочено в предложението за подобряване на JDK (JEP), тази ситуация изисква място в областта на Native Heap на паметта, за да се съхрани този обект. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. и думата за маркиране сега съхранява препратка към монитора като обект - няHowъв вътрешен обект на JVM. Както е посочено в предложението за подобряване на JDK (JEP), тази ситуация изисква място в областта на Native Heap на паметта, за да се съхрани този обект. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. Препратката към местоположението на паметта на този вътрешен обект ще бъде съхранена в маркиращата дума на заглавката на обекта. По този начин мониторът наистина е механизъм за синхронизиране на достъпа до споделени ресурси между множество нишки. JVM превключва между няколко реализации на този механизъм. Така че, за простота, когато говорим за монитор, всъщност говорим за ключалки. По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 7

Синхронизиран (изчаква се заключване)

Както видяхме по-рано, концепцията за "синхронизиран блок" (or "критична секция") е тясно свързана с концепцията за монитор. Разгледайте един пример:

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 секунди). През цялото това време задачата не може да продължи, защото не може да влезе в synchronizedблока, тъй като заключването вече е придобито. Ако нишката не може да получи ключалката, тя ще изчака монитора. Веднага щом получи заключването, ще продължи изпълнението. Когато нишка излезе от монитор, тя освобождава заключването. В JVisualVM изглежда така: По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 8Както можете да видите в JVisualVM, състоянието е „Монитор“, което означава, че нишката е блокирана и не може да вземе монитора. Можете също да използвате code, за да определите статуса на нишката, но имената на състоянието, определени по този начин, не съвпадат с имената, използвани в JVisualVM, въпреки че са подобни. В този случай,th1.getState()изразът в цикъла for ще върне БЛОКИРАН , тъй като докато цикълът работи, lockмониторът на обекта е зает от mainнишката и th1нишката е блокирана и не може да продължи, докато заключването не бъде освободено. В допълнение към синхронизираните блокове може да се синхронизира цял метод. Например, ето един метод от HashTableкласа:

public synchronized int size() {
	return count;
}
Този метод ще се изпълнява само от една нишка в даден момент. Наистина ли имаме нужда от ключалката? Да, имаме нужда от него. В случай на методи на екземпляр, обектът "този" (текущият обект) действа като заключване. Тук има интересна дискусия по тази тема: Има ли предимство използването на синхронизиран метод instead of синхронизиран блок? . Ако методът е статичен, тогава ключалката няма да бъде „този“ обект (защото няма „този“ обект за статичен метод), а по-скоро обект на клас (например, ) Integer.class.

Чакай (чака монитор). методи notify() и notifyAll().

Класът Thread има друг метод на изчакване, който е свързан с монитор. За разлика от sleep()и join(), този метод не може просто да бъде извикан. Името му е wait(). Методът waitсе извиква на обекта, свързан с монитора, който искаме да изчакаме. Да видим пример:

public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // The task object will wait until it is notified via lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // After we are notified, we will wait until we can acquire the lock
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // We sleep. Then we acquire the lock, notify, and release the lock
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
В JVisualVM изглежда така: По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 10За да разберете How работи това, не забравяйте, че методите wait()и notify()са свързани с java.lang.Object. Може да изглежда странно, че методите, свързани с нишки, са в Objectкласа. Но причината за това сега се разкрива. Спомняте си, че всеки обект в Java има заглавка. Заглавката съдържа различна информация за поддръжка, включително информация за монитора, т.е. състоянието на ключалката. Не забравяйте, че всеки обект or екземпляр на клас е свързан с вътрешна единица в JVM, наречена вътрешна ключалка or монитор. В горния пример codeът за обекта на задачата показва, че въвеждаме синхронизирания блок за монитора, свързан с обекта lock. Ако успеем да получим ключалката за този монитор, тогаваwait()е наречен. Нишката, изпълняваща задачата, ще освободи lockмонитора на обекта, но ще влезе в опашката от нишки, чакащи известие от lockмонитора на обекта. Тази опашка от нишки се нарича WAIT SET, което по-точно отразява нейната цел. Тоест, това е по-скоро набор, отколкото опашка. Нишката mainсъздава нова нишка с обекта задача, стартира я и изчаква 3 секунди. Това прави много вероятно новата нишка да може да получи заключването преди нишката mainи да влезе в опашката на монитора. След това mainсамата нишка влиза в lockсинхронизирания блок на обекта и извършва известяване на нишката с помощта на монитора. След като известието бъде изпратено, mainнишката освобождаваlockмонитора на обекта и новата нишка, която преди това чакаше lockосвобождаването на монитора на обекта, продължава изпълнението. Възможно е да изпратите известие само до една нишка ( notify()) or едновременно до всички нишки в опашката ( notifyAll()). Прочетете повече тук: Разлика между notify() и notifyAll() в Java . Важно е да се отбележи, че редът на уведомяване зависи от това How е внедрена JVM. Прочетете повече тук: Как да разрешите глада с notify и notifyAll? . Синхронизирането може да се извърши без да се указва обект. Можете да направите това, когато е синхронизиран цял метод, а не единичен блок code. Например за статични методи ключалката ще бъде обект на клас (получен чрез .class):

public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
По отношение на използването на брави и двата метода са еднакви. Ако методът не е статичен, тогава синхронизирането ще се извърши с помощта на текущия instance, тоест с помощта на this. Между другото, казахме по-рано, че можете да използвате getState()метода, за да получите състоянието на нишка. Например, за нишка в опашката, чакаща монитор, състоянието ще бъде WAITING or TIMED_WAITING, ако методът е wait()посочил таймаут. По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 11

https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java

Жизнен цикъл на нишка

В течение на живота си състоянието на нишката се променя. Всъщност тези промени обхващат жизнения цикъл на нишката. Веднага след като се създаде нишка, нейният статус е НОВ. В това състояние новата нишка все още не се изпълнява и програмата за планиране на нишки на Java все още не знае нищо за нея. За да може планировчикът на нишки да научи за нишката, трябва да извикате thread.start()метода. Тогава нишката ще премине в състояние RUNNABLE. В интернет има много неправилни диаграми, които правят разлика между състояния „Runnable“ и „Running“. Но това е грешка, защото Java не прави разлика между "готов за работа" (runnable) и "работещ" (running). Когато една нишка е жива, но не е активна (не е Runnable), тя е в едно от двете състояния:
  • БЛОКИРАН — изчакване за влизане в критичен раздел, т.е. блок synchronized.
  • WAITING — изчакване друга нишка да удовлетвори няHowво condition.
Ако conditionто е изпълнено, тогава планировчикът на нишки стартира нишката. Ако нишката чака до определено време, тогава състоянието й е TIMED_WAITING. Ако нишката вече не се изпълнява (тя е завършена or е хвърлено изключение), тогава тя влиза в състояние TERMINATED. За да разберете състоянието на нишка, използвайте getState()метода. Нишките също имат isAlive()метод, който връща true, ако нишката не е ПРЕКРАТЕНА.

LockSupport и паркиране на конци

Започвайки с Java 1.6, се появи интересен механизъм, наречен LockSupport . По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 12Този клас свързва "разрешение" с всяка нишка, която го използва. Извикването на park()метода се връща незабавно, ако разрешението е налично, консумирайки разрешението в процеса. В противен случай блокира. Извикването на unparkметода прави разрешението достъпно, ако все още не е налично. Има само 1 разрешително. Документацията на Java за 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) {
            // Request the permit and wait until we get it
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
Този code винаги ще чака, защото сега семафорът има 0 разрешения. И когато acquire()се извика в codeа (т.е. поиска разрешение), нишката изчаква, докато получи разрешението. Тъй като чакаме, трябва да се справим InterruptedException. Интересното е, че семафорът получава отделно състояние на нишка. Ако погледнем в JVisualVM, ще видим, че състоянието не е "Изчакай", а "Паркирай". По-добре заедно: Java и клас Thread.  Част II — Синхронизация - 13Нека да разгледаме друг пример:

public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            // Park the current thread
            System.err.println("Will be Parked");
            LockSupport.park();
            // As soon as we are unparked, we will start to act
            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);
}
Състоянието на нишката ще бъде WAITING, но JVisualVM прави разлика между waitключовата synchronizedдума и parkкласа LockSupport. Защо това е LockSupportтолкова важно? Обръщаме се отново към documentацията на Java и разглеждаме състоянието на нишката WAITING . Както можете да видите, има само три начина да влезете в него. Два от тези начини са 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чака някой да освободи споделения ресурс. Ако погледнем в JVisualVM, ще видим, че новата нишка ще бъде паркирана, докато нишката mainне освободи заключването към нея. Можете да прочетете повече за заключванията тук: Java 8 StampedLocks срещу ReadWriteLocks и API за синхронизиране и заключване в Java. За да разберете по-добре How се прилагат заключванията, е полезно да прочетете за Phaser в тази статия: Ръководство за Java Phaser . И като говорим за различни синхронизатори, трябва да прочетете статията на DZone за Синхронизаторите на Java.

Заключение

В този преглед разгледахме основните начини, по които нишките взаимодействат в Java. Допълнителен материал: По-добре заедно: Java и клас Thread. Част I — Нишки за изпълнение По-добре заедно: Java и класът Thread. Част III — Взаимодействието е по-добро заедно: Java и класът Thread. Част IV — Callable, Future и приятели По-добре заедно: Java и класът Thread. Част V — Executor, ThreadPool, Fork/Join Better заедно: Java и класът Thread. Част VI — Изстрелвай!
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION