JavaRush /Java блогу /Random-KY /Жаваны жип менен буза албайсыз: I бөлүк - Темалар
Viacheslav
Деңгээл

Жаваны жип менен буза албайсыз: 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убузда азыр олуттуу кемчorк бар. Эгерде биз эч кандай аргументти өткөрүп албасак (б.а. жөн гана java HelloWorldApp аткарсак), биз ката алабыз:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
деп аталган жипте өзгөчө жагдай (б.а. ката) пайда болду main. Javaда кандайдыр бир жиптер бар экен? Биздин саякатыбыз ушул жерден башталат.

Java жана темалар

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

java.lang.Thread

Java тorндеги жип класстын мисалы катары көрсөтүлөт 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 түрү бар: демондор жана демондор эмес. Демон жиптери – фондо кандайдыр бир иштерди аткарган фондук жиптер (кызмат жиптери). Бул кызыктуу термин "Максвелдин жинине" шилтеме болуп саналат, ал тууралуу " жиндер " жөнүндө Wikipedia макаласынан окуй аласыз . Документте айтылгандай, 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/ " карамадан көбүрөөк окуй аласыз . Мындан тышкары, жип артыкчылыкка ээ. Приоритеттер жөнүндө кененирээк төмөнкү бөлүмдөн окуй аласыз. макала " Multithreading Java Thread Priority ".

Жип түзүү

Документте айтылгандай, жипти түзүүнүн 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, жаңы жип башталbyte. Бул startJVMден жаңы жипти түзүүнү суранган ыкма. Thread тукумунун варианты начар, анткени биз Threadды класс иерархиясына киргизебиз. Экинчи кемчorги - биз “Жалгыз жоопкерчorк” СОЛИД принцибин буза баштадык, анткени биздин класс бир эле учурда жипти башкаруу үчүн да, бул жипте аткарылышы керек болгон кээ бир тапшырмалар үчүн да жооптуу болот. Кайсынысы туура? 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