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

По-добре заедно: Java и клас Thread. Част I — Нишки на изпълнение

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

Въведение

Многонишковостта е вградена в Java от самото начало. И така, нека разгледаме накратко това нещо, наречено многопоточност. По-добре заедно: Java и клас Thread.  Част I — Нишки на изпълнение - 1Ние приемаме официалния урок от Oracle като отправна точка: „ Урок: Приложението „Hello World!“ “. Ще променим леко codeа на нашата програма Hello World, Howто следва:
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsе масив от входни параметри, предавани при стартиране на програмата. Запазете този code във файл с име, което съответства на името на класа и има разширение .java. Компorрайте го с помощта на помощната програма javac : javac HelloWorldApp.java. След това изпълняваме нашия code с няHowъв параметър, например „Разбрано“: java HelloWorldApp Roger По-добре заедно: Java и клас Thread.  Част I — Нишки на изпълнение - 2Нашият code в момента има сериозен недостатък. Ако не подадете ниHowъв аргумент (т.е. изпълните само "java HelloWorldApp"), получаваме грешка:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Възникна изключение (т.е. грешка) в нишката с име "main". И така, Java има нишки? Това е мястото, където започва нашето пътуване.

Java и нишки

За да разберете Howво е нишка, трябва да разберете How стартира една Java програма. Нека променим нашия code, Howто следва:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			// Do nothing
		}
	}
}
Сега нека го компorраме отново с javac. За удобство ще изпълним нашия Java code в отделен прозорец. В Windows това може да стане по следния начин: start java HelloWorldApp. Сега ще използваме помощната програма jps , за да видим Howва информация може да ни каже Java: По-добре заедно: Java и клас Thread.  Част I — Нишки на изпълнение - 3Първото число е PID or ID на процеса. Какво е процес?
A process is a combination of code and data sharing a common virtual address space.
С процесите различните програми са изолирани една от друга, докато работят: всяко приложение използва своя собствена област в паметта, без да пречи на други програми. За да научите повече, препоръчвам да прочетете този урок: Процеси и нишки . Един процес не може да съществува без нишка, така че ако процес съществува, то той има поне една нишка. Но How става това в Java? Когато стартираме Java програма, изпълнението започва с mainметода. Сякаш стъпваме в програмата, така че този специален mainметод се нарича входна точка. Методът mainвинаги трябва да бъде "public static void", така че Java виртуалната машина (JVM) да може да започне да изпълнява нашата програма. За повече информация Защо главният метод на Java е статичен?. Оказва се, че програмата за стартиране на Java (java.exe or javaw.exe) е просто C приложение: то зарежда различните DLL файлове, които всъщност съставляват JVM. Java стартерът прави специфичен набор от извиквания на Java Native Interface (JNI). JNI е механизъм за свързване на света на виртуалната машина на Java със света на C++. Така че стартерът не е самата JVM, а по-скоро механизъм за нейното зареждане. Той знае правилните команди, които да изпълни, за да стартира JVM. Той знае How да използва JNI повиквания, за да настрои необходимата среда. Настройването на тази среда включва създаване на основната нишка, която се нарича "main", разбира се. За да илюстрираме по-добре кои нишки съществуват в Java процес, използваме jvisualvmинструмент, който е включен в JDK. Познавайки pid на даден процес, можем незабавно да видим информация за този процес: jvisualvm --openpid <process id> По-добре заедно: Java и клас Thread.  Част I — Нишки на изпълнение - 4Интересното е, че всяка нишка има своя отделна област в паметта, разпределена за процеса. Тази структура на паметта се нарича стек. Стекът се състои от рамки. Рамка представлява активирането на метод (незавършено извикване на метод). Една рамка може също да бъде представена като StackTraceElement (вижте Java API за StackTraceElement ). Можете да намерите повече информация относно паметта, разпределена за всяка нишка в дискусията тук: „ Как Java (JVM) разпределя стека за всяка нишка “. Ако погледнете Java API и потърсите думата "Thread", ще намерите java.lang.Threadклас. Това е класът, който представлява нишка в Java и ще трябва да работим с него. По-добре заедно: Java и клас Thread.  Част I — Нишки на изпълнение - 5

java.lang.Thread

В Java една нишка е представена от екземпляр на java.lang.Threadкласа. Веднага трябва да разберете, че екземплярите на класа Thread сами по себе си не са нишки за изпълнение. Това е просто един вид API за нишките на ниско ниво, управлявани от JVM и операционната система. Когато стартираме JVM с помощта на програмата за стартиране на Java, тя създава mainнишка, наречена "main", и няколко други нишки за поддръжка. Както е посочено в JavaDoc за класа Thread: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. Има 2 вида нишки: демони и недемони. Демон нишките са фонови (домакински) нишки, които вършат известна работа във фонов режим. Думата "демон" се отнася до демона на Максуел. Можете да научите повече в тази статия в Уикипедия . Както е посочено в documentацията, JVM продължава да изпълнява програмата (процеса), докато:
  • Извиква се методът Runtime.exit ().
  • Всички нишки НЕ-демон завършват работата си (без грешки or с хвърлени изключения)
От това следва важна подробност: демон нишките могат да бъдат прекратени във всяка точка. В резултат на това няма гаранции за целостта на техните данни. Съответно нишките на daemon са подходящи за определени домакински задачи. Например, Java има нишка, която е отговорна за обработката finalize()на извиквания на метод, т.е. нишки, свързани с Garbage Collector (gc). Всяка нишка е част от група ( ThreadGroup ). И групите могат да бъдат част от други групи, образувайки определена йерархия or структура.
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);
}
Делението на нула ще доведе до грешка, която ще бъде уловена от манипулатора. Ако не посочите свой собствен манипулатор, тогава JVM ще извика манипулатора по подразбиране, който ще изведе проследяването на стека на изключението към StdError. Всяка нишка също има приоритет. Можете да прочетете повече за приоритетите в тази статия: Java Thread Priority in Multithreading .

Създаване на нишка

Както е посочено в documentацията, имаме 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()метода. Не бъркайте тези методи: ако извикаме un()метода r директно, няма да бъде стартирана нова нишка. Това е start()методът, който иска от JVM да създаде нова нишка. Тази опция, при която наследяваме Thread, вече е лоша, тъй като включваме Thread в йерархията на нашите класове. Вторият недостатък е, че започваме да нарушаваме принципа на "една отговорност". Това означава, че нашият клас е едновременно отговорен за управлението на потока и за изпълнението на няHowва задача в този поток. Кой е правилният начин? Отговорът се намира в същия run()метод, който отменяме:
public void run() {
	if (target != null) {
		target.run();
	}
}
Ето targetнякои java.lang.Runnable, които можем да предадем, когато създаваме екземпляр на класа Thread. Това означава, че можем да направим следното:
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();
    }
}
Runnableсъщо е функционален интерфейс от Java 1.8. Това прави възможно да се напише още по-красив code за задача на нишка:
public static void main(String[] args) {
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Заключение

Надявам се, че тази дискусия изяснява Howво е нишка, How възникват нишките и Howви основни операции могат да се извършват с нишки. В следващата част ще се опитаме да разберем How нишките взаимодействат една с друга и ще изследваме жизнения цикъл на нишките. По-добре заедно: Java и клас Thread. Част II — По-добра синхронизация заедно: Java и класът Thread. Част III — Взаимодействието е по-добро заедно: Java и класът Thread. Част IV — Callable, Future и приятели По-добре заедно: Java и класът Thread. Част V — Executor, ThreadPool, Fork/Join Better заедно: Java и класът Thread. Част VI — Изстрелвай!
Коментари
  • Популярен
  • Нов
  • Стар
Трябва да сте влезли, за да оставите коментар
Тази страница все още няма коментари