JavaRush /Java blogi /Random-UZ /Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsi...
Viacheslav
Daraja

Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya

Guruhda nashr etilgan

Kirish

Shunday qilib, biz bilamizki, Java-da mavzular bor, ular haqida " Siz Java-ni mavzu bilan buzolmaydi: I qism - mavzular " sharhida o'qishingiz mumkin . Bir vaqtning o'zida ishni bajarish uchun iplar kerak. Shuning uchun, iplar qandaydir tarzda bir-biri bilan o'zaro ta'sir qilish ehtimoli juda katta. Keling, bu qanday sodir bo'lishini va bizda qanday asosiy nazorat borligini aniqlaymiz. Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 1

Yo'l bering

Thread.yield() usuli sirli va kamdan-kam qo'llaniladi. Internetda uning tavsifining ko'plab variantlari mavjud. Ba'zilar o'zlarining ustuvorliklarini hisobga olgan holda ip pastga siljiydigan iplarning qandaydir navbati haqida yozadilar. Kimdir ish zarrachasi o'z holatini ishlaydigandan ishga tushirilishiga o'zgartirishi haqida yozadi (garchi bu statuslarga bo'linish yo'q va Java ularni ajratmaydi). Lekin, aslida, hamma narsa ancha noma'lum va ma'lum ma'noda oddiyroq. Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 2Usul hujjatlari mavzusida " JDK-6416721: (spec thread) Fix Thread.yield() javadocyield " xatosi mavjud . Agar siz uni o'qigan bo'lsangiz, aslida bu usul faqat Java ish rejasini tuzuvchiga ushbu mavzuni bajarish uchun kamroq vaqt berilishi mumkinligi haqida ba'zi tavsiyalarni bildirishi aniq . Ammo aslida nima sodir bo'ladi, rejalashtiruvchi tavsiyani eshitadimi yoki umuman nima qilishi JVM va operatsion tizimni amalga oshirishga bog'liq. Yoki ba'zi boshqa omillar tufayli. Barcha chalkashliklar, ehtimol, Java tilini ishlab chiqish jarayonida multithreadingni qayta ko'rib chiqish bilan bog'liq. Batafsil " Java Thread.yield() ga qisqacha kirish " sharhida o'qishingiz mumkin. yield

Kutish - uxlab qolgan ip

Ishlash paytida ip uxlab qolishi mumkin. Bu boshqa iplar bilan o'zaro aloqaning eng oddiy turi. Java virtual mashinasi o'rnatilgan, Java kodi bajariladigan operatsion tizimda Thread Scheduler deb nomlangan o'z ish jadvali mavjud. Qaysi ipni qachon o'tkazishni o'zi hal qiladi. Dasturchi ushbu rejalashtiruvchi bilan to'g'ridan-to'g'ri Java kodidan o'zaro aloqada bo'lolmaydi, lekin u JVM orqali rejalashtiruvchidan mavzuni bir muddat to'xtatib turishini va "uyqu rejimiga o'tkazishni" so'rashi mumkin. Batafsil " Thread.sleep() " va " Multithreading qanday ishlaydi " maqolalarida o'qishingiz mumkin . Bundan tashqari, Windows OS da iplar qanday ishlashini bilib olishingiz mumkin: " Windows Threadning ichki qismi ". Endi biz buni o'z ko'zimiz bilan ko'ramiz. Quyidagi kodni faylga saqlaymiz 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();
    }
}
Ko'rib turganingizdek, bizda 60 soniya kutadigan vazifa bor, shundan so'ng dastur tugaydi. Biz kompilyatsiya qilamiz javac HelloWorldApp.javava ishga tushiramiz java HelloWorldApp. Alohida oynada ishga tushirish yaxshidir. Masalan, Windows-da bu shunday bo'ladi: start java HelloWorldApp. jps buyrug'idan foydalanib, biz jarayonning PID-ni topamiz va dan foydalanib mavzular ro'yxatini ochamiz jvisualvm --openpid pidПроцесса: Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 3Ko'rib turganingizdek, bizning ipimiz Uyqu holatiga kirdi. Aslida, joriy ipni uxlashni yanada chiroyli qilish mumkin:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Ehtimol, biz hamma joyda ishlov berishimizni payqadingizmi InterruptedException? Keling, nima uchun ekanligini tushunaylik.

Mavzuni uzish yoki Thread.interrupt

Gap shundaki, ip tushida kutayotganda, kimdir bu kutishni to'xtatmoqchi bo'lishi mumkin. Bunday holda, biz bunday istisno bilan shug'ullanamiz. Bu usul Thread.stopeskirgan deb e'lon qilinganidan keyin amalga oshirildi, ya'ni. eskirgan va foydalanish uchun istalmagan. Buning sababi, usul chaqirilganda, stopip oddiygina "o'ldirilgan", bu juda oldindan aytib bo'lmaydigan edi. Oqim qachon to'xtatilishini bila olmadik, ma'lumotlarning izchilligiga kafolat bera olmadik. Tasavvur qiling-a, siz faylga ma'lumot yozyapsiz va keyin oqim yo'q qilinadi. Shuning uchun, biz oqimni o'ldirmaslik, balki uni to'xtatish kerakligi haqida xabar berish mantiqan to'g'ri keladi, deb qaror qildik. Bunga qanday munosabatda bo'lish oqimning o'ziga bog'liq. Batafsil ma'lumotni Oracle'ning " Thred.stop nima uchun eskirgan? " Keling, bir misolni ko'rib chiqaylik:
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();
}
Ushbu misolda biz 60 soniya kutmaymiz, lekin darhol "Uzilgan" ni chop qilamiz. Buning sababi, biz ipning usulini chaqirdik interrupt. Ushbu usul "uzilish holati deb ataladigan ichki bayroq" ni o'rnatadi. Ya'ni, har bir ip to'g'ridan-to'g'ri kirish imkoni bo'lmagan ichki bayroqqa ega. Ammo bizda ushbu bayroq bilan ishlashning mahalliy usullari mavjud. Lekin bu yagona yo'l emas. Ip bajarilish jarayonida bo'lishi mumkin, biror narsani kutmasdan, balki shunchaki harakatlarni bajaradi. Ammo bu ular o'z ishining ma'lum bir nuqtasida uni yakunlashni xohlashlarini ta'minlashi mumkin. Masalan:
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();
}
Yuqoridagi misolda siz whileip tashqi tomondan uzilib qolguncha tsiklning ishlashini ko'rishingiz mumkin. IsInterrupted bayrog'i haqida bilish kerak bo'lgan muhim narsa shundaki, agar biz uni ushlasak InterruptedException, bayroq isInterruptedqayta o'rnatiladi va keyin isInterruptedu noto'g'ri bo'ladi. Bundan tashqari, Thread klassi uchun statik usul mavjud bo'lib, u faqat joriy mavzuga tegishli - Thread.interrupted() , lekin bu usul bayroqni noto'g'ri holatga qaytaradi! Batafsil ma'lumotni " Mavzuning uzilishi " bo'limida o'qishingiz mumkin .

Qo‘shilish — boshqa mavzu tugashini kutmoqda

Kutishning eng oddiy turi boshqa mavzu tugashini kutishdir.
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");
}
Ushbu misolda yangi ip 5 soniya davomida uxlaydi. Shu bilan birga, asosiy ip uxlab yotgan ip uyg'onguncha va o'z ishini tugatguncha kutadi. Agar siz JVisualVM orqali qarasangiz, ipning holati quyidagicha ko'rinadi: Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 4Monitoring vositalari tufayli ip bilan nima sodir bo'layotganini ko'rishingiz mumkin. Usul joinjuda oddiy, chunki bu oddiygina java kodi bo'lgan usul bo'lib, u waitchaqirilgan ip tirik bo'lganda bajariladi. Ip o'lgandan keyin (tugatishda), kutish tugaydi. Bu usulning butun sehridir join. Shuning uchun, keling, eng qiziqarli qismga o'tamiz.

Kontseptsiya monitori

Multithreadingda Monitor kabi narsa mavjud. Umuman olganda, monitor so'zi lotin tilidan "nazoratchi" yoki "nazoratchi" deb tarjima qilingan. Ushbu maqola doirasida biz mohiyatni eslab qolishga harakat qilamiz va istaganlar uchun batafsil ma'lumot uchun havolalardan materialga sho'ng'ishingizni so'rayman. Sayohatimizni Java tili spetsifikatsiyasidan, ya'ni JLS bilan boshlaylik: " 17.1. Sinxronizatsiya ". Unda quyidagilar aytiladi: Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 5Ma'lum bo'lishicha, Java mavzular o'rtasida sinxronizatsiya qilish uchun "Monitor" deb nomlangan ma'lum bir mexanizmdan foydalanadi. Har bir ob'ekt u bilan bog'langan monitorga ega va iplar uni qulflashi yoki qulfini ochishi mumkin. Keyinchalik, biz Oracle veb-saytida o'quv qo'llanmasini topamiz: " Ichki qulflar va sinxronizatsiya ". Ushbu qo'llanmada Java-da sinxronizatsiya ichki blokirovka yoki monitor blokirovkasi deb nomlanuvchi ichki ob'ekt atrofida qurilganligi tushuntiriladi. Ko'pincha bunday qulf oddiygina "monitor" deb ataladi. Yana shuni ko'ramizki, Java-dagi har bir ob'ekt u bilan bog'langan ichki qulfga ega. Siz " Java - ichki qulflar va sinxronizatsiya " ni o'qishingiz mumkin. Keyinchalik, Java-dagi ob'ektni monitor bilan qanday bog'lash mumkinligini tushunish muhimdir. Java-dagi har bir ob'ekt sarlavhaga ega - dasturchi uchun koddan mavjud bo'lmagan, lekin virtual mashina ob'ektlar bilan to'g'ri ishlashi uchun zarur bo'lgan ichki metama'lumotlarning bir turi. Ob'ekt sarlavhasi quyidagicha ko'rinadigan MarkWord ni o'z ichiga oladi: Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 6

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

Habrning maqolasi bu erda juda foydali: " Ammo ko'p ish zarralari qanday ishlaydi? I qism: sinxronizatsiya ." Ushbu maqolaga JDK bugtaker-dan topshiriq blokining Xulosasidan tavsifni qo'shish kerak: “ JDK-8183909 ”. Xuddi shu narsani " JEP-8183909 " da o'qishingiz mumkin . Shunday qilib, Java-da monitor ob'ekt bilan bog'langan va ip bu ipni bloklashi mumkin yoki ular ham "qulflash" deyishadi. Eng oddiy misol:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Shunday qilib, kalit so'zdan foydalanib, synchronizedjoriy ip (bu kod satrlari bajarilgan) ob'ekt bilan bog'langan monitordan foydalanishga harakat qiladi objectva "qulflash" yoki "monitorni qo'lga olish" (ikkinchi variant hatto afzalroqdir). Agar monitor uchun hech qanday qarama-qarshilik bo'lmasa (ya'ni, boshqa hech kim bir xil ob'ektda sinxronlashni xohlamasa), Java "biased locking" deb nomlangan optimallashtirishni amalga oshirishga harakat qilishi mumkin. Mark Word-dagi ob'ektning sarlavhasida mos teg va monitor qaysi ipga biriktirilganligi yozuvi bo'ladi. Bu monitorni suratga olishda ortiqcha xarajatlarni kamaytiradi. Agar monitor allaqachon boshqa ipga ulangan bo'lsa, unda bu qulflash etarli emas. JVM keyingi qulflash turiga o'tadi - asosiy qulflash. U solishtirish va almashtirish (CAS) operatsiyalaridan foydalanadi. Shu bilan birga, Mark Word-dagi sarlavha endi Mark Word-ning o'zini saqlamaydi, lekin uni saqlash uchun havola + teg o'zgartiriladi, shunda JVM biz asosiy qulflashdan foydalanayotganimizni tushunadi. Agar bir nechta iplarning monitorida kelishmovchilik bo'lsa (biri monitorni ushlab oldi, ikkinchisi monitorning chiqishini kutmoqda), u holda Mark Word-dagi teg o'zgaradi va Mark Word monitorga havolani saqlashni boshlaydi. ob'ekt - JVM ning ba'zi ichki ob'ekti. JEPda aytilganidek, bu holda ushbu ob'ektni saqlash uchun Native Heap xotira maydonida bo'sh joy talab qilinadi. Ushbu ichki ob'ektning saqlash joyiga havola Mark Word ob'ektida joylashgan bo'ladi. Shunday qilib, biz ko'rib turganimizdek, monitor haqiqatan ham umumiy resurslarga bir nechta oqimlarning kirishini sinxronlashtirishni ta'minlaydigan mexanizmdir. JVM o'rtasida almashinadigan ushbu mexanizmning bir nechta ilovalari mavjud. Shuning uchun, oddiylik uchun, monitor haqida gapirganda, biz aslida qulflar haqida gapiramiz. Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 7

Sinxronlashtirilgan va qulf bilan kutish

Monitor kontseptsiyasi, biz ilgari ko'rganimizdek, "sinxronizatsiya bloki" tushunchasi bilan chambarchas bog'liq (yoki uni tanqidiy qism deb ham atashadi). Keling, bir misolni ko'rib chiqaylik:
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(" ...");
	}
}
Bu erda asosiy ip birinchi navbatda vazifani yangi ipga yuboradi, so'ngra darhol qulfni "qo'lga oladi" va u bilan uzoq operatsiyani bajaradi (8 soniya). Bu vaqt davomida vazifa uning bajarilishi uchun blokga kira olmaydi synchronized, chunki qulf allaqachon band. Agar ip qulfni ololmasa, u uni monitorda kutadi. Qabul qilishi bilanoq u ijroni davom ettiradi. Ip monitordan chiqib ketganda, u qulfni chiqaradi. JVisualVM da u quyidagicha ko'rinadi: Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 8Ko'rib turganingizdek, JVisualVM dagi holat "Monitor" deb ataladi, chunki ip bloklangan va monitorni egallay olmaydi. Siz koddagi ipning holatini ham bilib olishingiz mumkin, ammo bu holat nomi JVisualVM shartlariga mos kelmaydi, garchi ular o'xshash bo'lsa ham. Bunday holda, th1.getState()tsikl BLOCKED nifor qaytaradi , chunki Loop ishlayotganda, monitor ip bilan band bo'ladi va ip bloklanadi va qulf qaytarilmaguncha ishlashni davom ettira olmaydi. Sinxronizatsiya bloklariga qo'shimcha ravishda, butun usul ham sinxronlashtirilishi mumkin. Masalan, sinfdan bir usul : lockmainth1HashTable
public synchronized int size() {
	return count;
}
Bir birlik vaqt ichida bu usul faqat bitta ip tomonidan bajariladi. Lekin bizga qulf kerak, to'g'rimi? Ha, menga kerak. Ob'ekt usullari bo'lsa, qulf bo'ladi this. Ushbu mavzu bo'yicha qiziqarli munozara mavjud: " Sinxronlashtirilgan blok o'rniga Sinxronlashtirilgan usuldan foydalanishning afzalligi bormi? ". Agar usul statik bo'lsa, u holda blokirovka bo'lmaydi this(chunki statik usul uchun bo'lishi mumkin emas this), balki sinf ob'ekti (Masalan, Integer.class).

Kuting va monitorda kuting. Ogohlantirish va bildirishning barcha usullari

Threadda monitorga ulangan boshqa kutish usuli mavjud. sleepva dan farqli o'laroq join, uni shunchaki chaqirib bo'lmaydi. Va uning ismi wait. Usul waitbiz monitorida kutmoqchi bo'lgan ob'ektda bajariladi. Keling, misolni ko'rib chiqaylik:
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-da u quyidagicha ko'rinadi: Bu qanday ishlashini tushunish uchun usullar ga murojaat qilishini Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 10yodda tutishingiz kerak . Mavzu bilan bog'liq usullarning mavjudligi g'alati tuyuladi . Lekin bu erda javob yotadi. Esda tutganimizdek, Java-dagi har bir ob'ekt sarlavhaga ega. Sarlavhada turli xil xizmat ma'lumotlari, jumladan, monitor haqidagi ma'lumotlar - blokirovka holati haqidagi ma'lumotlar mavjud. Va biz eslayotganimizdek, har bir ob'ekt (ya'ni, har bir misol) ichki blokirovka deb ataladigan ichki JVM ob'ekti bilan bog'langan, u ham monitor deb ataladi. Yuqoridagi misolda vazifa biz bilan bog'langan monitorda sinxronizatsiya blokiga kirishimizni tasvirlaydi . Agar ushbu monitorda qulfni olish mumkin bo'lsa, u holda . Ushbu vazifani bajaruvchi ip monitorni chiqaradi , lekin monitörde bildirishnomani kutayotgan iplar navbatiga qo'shiladi . Ushbu iplar navbati WAIT-SET deb ataladi, bu esa mohiyatni to'g'riroq aks ettiradi. Bu navbatdan ko'ra ko'proq to'plam. Ip vazifa vazifasi bilan yangi ip yaratadi, uni ishga tushiradi va 3 soniya kutadi. Bu yuqori ehtimollik bilan yangi ipga ipdan oldin qulfni ushlab , monitorda navbatda turishga imkon beradi. Shundan so'ng, ipning o'zi sinxronizatsiya blokiga kiradi va monitorda ip haqida xabar beradi. Bildirishnoma yuborilgandan so'ng, ip monitorni chiqaradi va yangi oqim (ilgari kutilgan) monitorning chiqarilishini kutgandan so'ng ishlashni davom ettiradi. Xabarnomani faqat bitta mavzuga ( ) yoki navbatdagi barcha mavzularga bir vaqtning o'zida yuborish mumkin ( ). Batafsil " Java'da notify() va notifyAll() o'rtasidagi farq " bo'limida o'qishingiz mumkin . Shuni ta'kidlash kerakki, bildirishnomalar tartibi JVMni amalga oshirishga bog'liq. Batafsil ma'lumotni " Ochlikni notify and notifyall bilan qanday hal qilish mumkin? ". Sinxronizatsiya ob'ektni ko'rsatmasdan amalga oshirilishi mumkin. Buni kodning alohida bo'limi emas, balki butun usul sinxronlashtirilganda amalga oshirilishi mumkin. Masalan, statik usullar uchun blok sinf ob'ekti bo'ladi (orqali orqali olinadi ): waitnotifyjava.lang.ObjectObjectlockwaitlocklockmainmainmainlockmainlocklocknotifynotifyAll.class
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
Qulflarni ishlatish nuqtai nazaridan ikkala usul ham bir xil. Agar usul statik bo'lmasa, u holda sinxronizatsiya joriy bo'yicha amalga oshiriladi instance, ya'ni this. Aytgancha, avvalroq biz ushbu usul yordamida getStateipning holatini olishingiz mumkinligini aytdik. waitDemak, monitor tomonidan navbatga qoʻyilgan ip mavjud, agar usul kutish vaqti chegarasini koʻrsatsa, holat KUTISH yoki TIMED_WAITING boʻladi .Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 11

Ipning hayot aylanishi

Ko'rib turganimizdek, oqim hayot davomida o'z maqomini o'zgartiradi. Aslida, bu o'zgarishlar ipning hayot aylanishidir. Mavzu endigina yaratilganda, u YANGI maqomiga ega bo'ladi. Bu holatda, u hali boshlanmagan va Java Thread Scheduler yangi mavzu haqida hali hech narsa bilmaydi. Tarmoq rejalashtiruvchisi mavzu haqida bilishi uchun siz thread.start(). Keyin ip RUNNABLE holatiga o'tadi. Internetda Runnable va Running holatlari ajratilgan ko'plab noto'g'ri sxemalar mavjud. Lekin bu xato, chunki... Java "ishlash uchun tayyor" va "ishlash" holatini farqlamaydi. Agar ip jonli, lekin faol bo'lmasa (Runnable emas), u ikkita holatdan birida bo'ladi:
  • BLOCKED - himoyalangan bo'limga kirishni kutadi, ya'ni. blokga synchonized.
  • WAITING - shart asosida boshqa ipni kutadi. Agar shart to'g'ri bo'lsa, ipni rejalashtiruvchi ipni boshlaydi.
Agar mavzu vaqt bo'yicha kutayotgan bo'lsa, u TIMED_WAITING holatida. Agar ip endi ishlamasa (muvaffaqiyatli tugallangan yoki istisno bilan), u TO'XTILGAN holatga o'tadi. Ipning holatini (uning holatini) bilish uchun usul ishlatiladi getState. isAliveMavzular, shuningdek , agar ip tugatilmasa, true qiymatini qaytaradigan usulga ega .

LockSupport va to'xtash joyi

Java 1.6 dan boshlab LockSupport deb nomlangan qiziqarli mexanizm mavjud edi . Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 12Bu sinf "ruxsat" yoki ruxsatni undan foydalanadigan har bir oqim bilan bog'laydi. parkQo'ng'iroq paytida xuddi shu ruxsatni egallagan ruxsat mavjud bo'lsa, usul chaqiruvi darhol qaytariladi. Aks holda u bloklanadi. Usulni chaqirish, unparkagar u allaqachon mavjud bo'lmasa, ruxsatni beradi. Faqat 1 ta ruxsatnoma mavjud Java API da LockSupportma'lum Semaphore. Keling, oddiy misolni ko'rib chiqaylik:
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!");
    }
}
Bu kod abadiy kutadi, chunki semaforda endi 0 ruxsatnomasi bor. Va kod bilan chaqirilganda acquire(ya'ni, ruxsat so'raganda), ip ruxsat olguncha kutadi. Biz kutayotganimiz sababli, biz uni qayta ishlashga majburmiz InterruptedException. Qizig'i shundaki, semafor alohida ip holatini amalga oshiradi. Agar biz JVisualVM ga qarasak, bizning davlatimiz Wait emas, balki Park ekanligini ko'ramiz. Siz Java-ni ip bilan buzolmaysiz: II qism - sinxronizatsiya - 13Keling, yana bir misolni ko'rib chiqaylik:
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);
}
Mavzu holati KUTILADI, lekin JVisualVM waitdan synchronizedva parkdan farq qiladi LockSupport. Nima uchun bu juda muhim LockSupport? Keling, yana Java API-ga qaytaylik va Thread State WAITING ni ko'rib chiqaylik . Ko'rib turganingizdek, unga kirishning faqat uchta usuli bor. 2 yo'l - bu waitva join. Va uchinchisi LockSupport. Java-dagi qulflar bir xil printsiplar asosida qurilgan LockSupportva yuqori darajadagi vositalarni ifodalaydi. Keling, bittasini ishlatishga harakat qilaylik. Keling, masalan, quyidagi manzilni ko'rib chiqaylik 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();
    }
}
Oldingi misollarda bo'lgani kabi, bu erda hamma narsa oddiy. lockkimdir resurs chiqarishini kutadi. mainAgar biz JVisualVM ni ko'rib chiqsak, biz ip unga qulfni bermaguncha yangi ip to'xtab turishini ko'ramiz . Qulflar haqida ko'proq ma'lumotni bu yerda o'qishingiz mumkin: " Java 8 da ko'p tarmoqli dasturlash. Ikkinchi qism. O'zgaruvchan ob'ektlarga kirishni sinxronlash " va " Java Lock API. Foydalanish nazariyasi va misoli ." Qulflarni amalga oshirishni yaxshiroq tushunish uchun " Phaser Class " sharhida Phazer haqida o'qish foydali bo'ladi . Turli xil sinxronizatorlar haqida gapiradigan bo'lsak, siz Habré-dagi " Java.util.concurrent.* Sinxronizatorlar ma'lumotnomasi " maqolasini o'qishingiz kerak .

Jami

Ushbu sharhda biz Java-da iplarning o'zaro ta'sirining asosiy usullarini ko'rib chiqdik. Qo'shimcha material: #Viacheslav
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION