JavaRush /Java блогы /Random-KK /Сіз Java тілін жіппен бүлдіре алмайсыз: I бөлім - Тақырып...
Viacheslav
Деңгей

Сіз Java тілін жіппен бүлдіре алмайсыз: I бөлім - Тақырыптар

Топта жарияланған

Кіріспе

Multithreading Java-ға алғашқы күндерінен бастап енгізілген. Ендеше, көп ағынның не туралы екенін жылдам қарастырайық. Сіз Java-ны жіппен бұза алмайсыз: I бөлім - Тақырыптар - 1Бастапқы нүкте ретінде Oracle ресми сабағын алайық: « Сабақ: «Сәлем әлем!» қолданбасы ». Hello World қолданбасының codeын аздап келесіге өзгертейік:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsбағдарлама іске қосылғанда берілген енгізу параметрлерінің массиві болып табылады. Бұл codeты сыныптың аты мен кеңейтіміне сәйкес келетін аты бар файлға сақтайық .java. Javac утorтасын қолданып компиляция жасайық : javac HelloWorldApp.java Осыдан кейін codeты қандай да бір параметрмен шақырыңыз, мысалы, Роджер: java HelloWorldApp Roger Сіз Java-ны жіппен бұза алмайсыз: I бөлім - Тақырыптар - 2Біздің codeта қазір елеулі ақау бар. Егер біз ешқандай аргумент жібермесек (яғни java HelloWorldApp-ты орындасақ), біз қатені аламыз:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
деп аталатын ағында ерекше жағдай (яғни қате) орын алды main. Java-да қандай да бір ағындар бар екен? Міне, біздің саяхатымыз басталады.

Java және ағындар

Жіптің не екенін түсіну үшін Java қолданбасы қалай іске қосылатынын түсіну керек. Кодымызды келесідей өзгертейік:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
Енді оны javac көмегімен қайтадан құрастырайық. Әрі қарай, ыңғайлы болу үшін біз Java codeын бөлек терезеде іске қосамыз. Windows жүйесінде мұны келесідей орындауға болады: start java HelloWorldApp. Енді jps утorтасын пайдаланып , Java бізге қандай ақпаратты айтатынын көрейік: Сіз Java-ны жіппен бұза алмайсыз: I бөлім - Threads - 3Бірінші сан - PID немесе Процесс идентификаторы, процесс идентификаторы. Процесс дегеніміз не?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
Процестердің көмегімен әртүрлі бағдарламалардың орындалуы бір-бірінен оқшауланады: әрбір қосымша басқа бағдарламаларға кедергі жасамай, өзінің жеке жады аймағын пайдаланады. Мен сізге мақаланы толығырақ оқуға кеңес беремін: " https://habr.com/post/164487/ ". Процесс ағындарсыз өмір сүре алмайды, сондықтан процесс бар болса, онда кем дегенде бір ағын бар. Бұл Java-да қалай болады? Java бағдарламасын іске қосқан кезде оның орындалуы main. Біз бағдарламаға кіреміз, сондықтан бұл арнайы әдіс mainкіру нүктесі немесе «кіру нүктесі» деп аталады. Әдіс әрқашан Java виртуалды машинасы (JVM) бағдарламамызды орындай алатындай mainболуы керек . public static voidҚосымша мәліметтер алу үшін « Java негізгі әдісі неге статикалық? » бөлімін қараңыз. Java іске қосу құралы (java.exe немесе javaw.exe) қарапайым қолданба болып табылады (қарапайым C қолданбасы): ол әртүрлі DLL файлдарын жүктейді, олар шын мәнінде JVM. Java іске қосу құралы Java Native Interface (JNI) қоңырауларының белгілі бір жинағын жасайды. JNI — Java виртуалды машинасының әлемі мен C++ әлемін байланыстыратын механизм. Іске қосу құралы JVM емес, оның жүктеушісі болып шықты. Ол JVM іске қосу үшін орындалатын дұрыс пәрмендерді біледі. JNI қоңыраулары арқылы барлық қажетті ортаны қалай ұйымдастыру керектігін біледі. Қоршаған ортаны бұл ұйымдастыру әдетте деп аталатын негізгі жіпті құруды да қамтиды main. Java процесінде қандай ағындар өмір сүретінін нақтырақ көру үшін JDK құрамына кіретін jvisualvm бағдарламасын қолданамыз. Процестің pid мәнін біле отырып, біз ол туралы деректерді бірден аша аламыз: jvisualvm --openpid айдипроцесса Сіз Java-ны жіппен бұза алмайсыз: I бөлім - Тақырыптар - 4Бір қызығы, әрбір ағынның процесс үшін бөлінген жадтағы жеке аймағы бар. Бұл жады құрылымы стек деп аталады. Стек кадрлардан тұрады. Фрейм - бұл әдісті шақыру нүктесі, орындау нүктесі. Жақтауды StackTraceElement ретінде де көрсетуге болады ( StackTraceElement үшін Java API қараңыз ). Әрбір ағынға бөлінген жад туралы толығырақ мына жерден оқи аласыз . Егер Java API- ге қарап , Thread сөзін іздесек, java.lang.Thread класы бар екенін көреміз . Дәл осы класс Java тіліндегі ағынды көрсетеді және біз осымен жұмыс істеуіміз керек. Thread'ом Java не испортишь: Часть I — потоки - 5

java.lang.Thread

Java тіліндегі ағын класс данасы ретінде ұсынылған java.lang.Thread. Java тіліндегі Thread класының даналарының өзі ағындар емес екенін бірден түсінген жөн. Бұл JVM және операциялық жүйе басқаратын төменгі деңгейлі ағындарға арналған API түрі ғана. JVM-ді java іске қосу құралы арқылы іске қосқан кезде, ол атауы бар негізгі ағынды mainжәне тағы бірнеше қызмет ағынын жасайды. Thread класының JavaDoc бағдарламасында айтылғандай: When a Java Virtual Machine starts up, there is usually a single non-daemon thread ағындардың 2 түрі бар: демондар және демондар емес. Демон ағындары фондық режимде кейбір жұмыстарды орындайтын фондық ағындар (қызметтік ағындар). Бұл қызықты термин «Максвеллдің жынына» сілтеме болып табылады, ол туралы толығырақ « жындар » туралы Уикипедия мақаласынан оқи аласыз . Құжаттамада көрсетілгендей, JVM келесіге дейін бағдарламаны (процесті) орындауды жалғастырады:
  • Runtime.exit әдісі шақырылмаған
  • Барлық демон емес ағындар өз жұмысын аяқтады (қатесіз де, шығарылған ерекшеліктермен де)
Сондықтан маңызды деталь: демон ағындарын орындалатын кез келген пәрменде тоқтатуға болады. Сондықтан олардағы деректердің тұтастығына кепілдік берілмейді. Сондықтан демон ағындары кейбір қызмет тапсырмалары үшін қолайлы. Мысалы, Java-да қоқыс жинағышқа (GC) қатысты соңғы әдістерді немесе ағындарды өңдеуге жауап беретін ағын бар. Әрбір ағын қандай да бір топқа жатады ( ThreadGroup ). Ал топтар қандай да бір иерархияны немесе құрылымды құра отырып, бір-біріне кіре алады.
public static void main(String []args){
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
Топтар ағындарды басқаруды оңтайландыруға және оларды бақылауға мүмкіндік береді. Топтардан басқа, ағындардың өзіндік ерекшелік өңдегіші бар. Мысал қарастырайық:
public static void main(String []args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
Нөлге бөлу өңдеуші ұстайтын қатені тудырады. Өңдеушіні өзіңіз көрсетпесеңіз, StdError ішінде қателер сағын көрсететін әдепкі өңдегішті іске асыру жұмыс істейді. Толығырақ http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ " шолудан оқи аласыз . Сонымен қатар, жіптің басымдығы бар. Басымдықтар туралы қосымша ақпаратты мына жерден оқи аласыз. мақала « Көп ағындағы Java ағынының басымдығы ».

Жіп құру

Құжаттамада айтылғандай, бізде ағын жасаудың 2 жолы бар. Біріншісі - мұрагерді құру. Мысалы:
public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    }

    public static void main(String []args){
        Thread thread = new MyThread();
        thread.start();
    }
}
Көріп отырғаныңыздай, тапсырма әдісте іске қосылады run, ал ағын әдісте іске қосылады start. Оларды шатастырмау керек, өйткені... егер әдісті runтікелей іске қоссақ, жаңа ағын басталмайды. Бұл startJVM-ден жаңа ағын жасауды сұрайтын әдіс. Thread ұрпағы бар опция нашар, себебі біз сынып иерархиясына Thread қосамыз. Екінші кемшілік – біз «Жауапкершіліктің жалғыз» принципін бұза бастадық, өйткені біздің сынып бір уақытта ағынды басқаруға да, осы ағында орындалуы керек кейбір тапсырмаға да жауапты болады. Қайсысы дұрыс? runЖауап біз қайта анықтайтын әдісте :
public void run() {
	if (target != null) {
		target.run();
	}
}
Міне , класс данасын жасау кезінде Thread-ге беруге болатын кейбіреулер target. java.lang.RunnableСондықтан біз мұны істей аламыз:
public class HelloWorld{
    public static void main(String []args){
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Ол сонымен қатар RunnableJava 1.8 нұсқасынан бері функционалды интерфейс болып табылады. Бұл ағындар үшін тапсырма codeын одан да әдемі жазуға мүмкіндік береді:
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Барлығы

Сонымен, бұл оқиғадан ағынның не екенін, олардың қалай бар екенін және олармен қандай негізгі операцияларды орындауға болатынын түсінемін деп үміттенемін. Келесі бөлімде жіптердің бір-бірімен қалай әрекеттесетінін және олардың өмірлік циклі қандай екенін түсінген жөн. #Вячеслав
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION