Java 101: Разбиране на нишките Java, Част 4: Групи нишки, променливост и локални променливи на нишки

Този месец Java 101 завършва поредицата нишки, като се фокусира върху групи нишки, променливост, локални променливи на нишки, таймери и ThreadDeathкласа.

Разбиране на нишките на Java - прочетете цялата поредица

  • Част 1: Представяне на нишки и изпълними
  • Част 2: Синхронизация на нишки
  • Част 3: Планиране на нишки, изчакване / уведомяване и прекъсване на нишки
  • Част 4: Групи нишки, волатилност, локални променливи на нишки, таймери и смърт на нишки

Групи нишки

В програма за мрежов сървър една нишка чака и приема заявки от клиентски програми за изпълнение, например транзакции с бази данни или сложни изчисления. Нишката обикновено създава нова нишка за обработка на заявката. В зависимост от обема на заявката могат да присъстват едновременно много различни нишки, което усложнява управлението на нишките. За да опростят управлението на нишките, програмите организират своите нишки с групи нишки - java.lang.ThreadGroupобекти, които групират свързани обекти на нишки ThreadThreadподклас). Например вашата програма може да използва, за ThreadGroupда групира всички нишки за печат в една група.

Забележка: За да улесним дискусията, аз се позовавам на групи нишки, сякаш организират нишки. В действителност групите нишки организират ThreadThreadподклас) обекти, свързани с нишки.

Java изисква всяка нишка и всяка група нишки - запазете основната група нишки - за systemда се присъедини към друга група нишки. Тази подредба води до йерархична структура от групи нишки, която фигурата по-долу илюстрира в контекста на приложението.

В горната част на структурата на фигурата е групата systemконци. Създадената от JVM systemгрупа организира JVM нишки, които се занимават с финализиране на обекти и други системни задачи и служи като основна група от нишки на йерархичната структура на нишките на приложението. Точно отдолу systemе създадената от JVM mainгрупа нишки, която е systemгрупата на нишките (накратко подгрупа). mainсъдържа поне една нишка - създадената от JVM основна нишка, която изпълнява инструкции за байтов код в main()метода.

Под mainгрупата се намират подгрупите subgroup 1и subgroup 2подгрупи, създадени от приложения (които създава приложението на фигурата). Освен това subgroup 1групи три приложения създадени теми: thread 1, thread 2, и thread 3. За разлика от subgroup 2групи едно приложение, създадено резба: my thread.

След като вече знаете основите, нека започнем да създаваме групи нишки.

Създайте групи нишки и свържете нишки с тези групи

Документацията ThreadGroupза SDK на класа разкрива два конструктора: ThreadGroup(String name)и ThreadGroup(ThreadGroup parent, String name). И двата конструктора създават група нишки и й дават име, както nameпараметърът определя. Конструкторите се различават по избора си коя група нишки служи като родител на новосъздадената група нишки. Всяка група нишки, с изключение system, трябва да има родителска група нишки. Защото ThreadGroup(String name)родителят е групата нишки на нишката, която извиква ThreadGroup(String name). Като пример, ако основната нишка извика ThreadGroup(String name), новосъздадената група нишки има родителска група на основната нишка— main. Защото ThreadGroup(ThreadGroup parent, String name)родителят е групата, която се parentпозовава. Следващият код показва как да използвате тези конструктори за създаване на двойка групи нишки:

public static void main (String [] args) { ThreadGroup tg1 = new ThreadGroup ("A"); ThreadGroup tg2 = new ThreadGroup (tg1, "B"); }

В горния код основната нишка създава две групи нишки: Aи B. Първо, основната нишка създава Aчрез извикване ThreadGroup(String name). Най tg1-майка групата -referenced нишка е main, защото mainе нишка група основната нишка е. Второ, основната нишка създава Bчрез извикване ThreadGroup(ThreadGroup parent, String name). В tg2родител групата -referenced нишка е Aзащото tg1е позоваване преминава като аргумент ThreadGroup (tg1, "B")и Aасоциирани с tg1.

Съвет: След като вече няма нужда йерархия на ThreadGroupобекти, обадете се ThreadGroupе void destroy()метод, чрез позоваване на ThreadGroupобекта в горната част на тази йерархия. Ако в горния ThreadGroupобект и във всички обекти на подгрупи липсват обекти на нишки, destroy()подготвя тези обекти на групи нишки за събиране на боклука. В противен случай destroy()хвърля IllegalThreadStateExceptionобект. Докато не анулирате препратката към горния ThreadGroupобект (ако приемем, че променливата на полето съдържа тази препратка), събирачът на боклук не може да събере този обект. Позовавайки се на най-горния обект, можете да определите дали преди това е било осъществено destroy()извикване на метода чрез извикване ThreadGroupна boolean isDestroyed()метода Този метод връща вярно, ако йерархията на група нишки е била унищожена.

Сами по себе си групите нишки са безполезни. За да бъдат от полза, те трябва да групират нишки. Групирате нишки в групи нишки, като предавате ThreadGroupпрепратки към подходящи Threadконструктори:

ThreadGroup tg = new ThreadGroup ("subgroup 2"); Thread t = new Thread (tg, "my thread");

Кодът по-горе първо създава subgroup 2група с mainкато родителска група. (Предполагам, че основната нишка изпълнява кода.) Следващият код създава my threadThreadобект в subgroup 2групата.

Сега, нека създадем приложение, което създава йерархичната структура на нишката на групата ни:

Листинг 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("subgroup 1"); Thread t1 = new Thread (tg, "thread 1"); Thread t2 = new Thread (tg, "thread 2"); Thread t3 = new Thread (tg, "thread 3"); tg = new ThreadGroup ("subgroup 2"); Thread t4 = new Thread (tg, "my thread"); tg = Thread.currentThread ().getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Active thread groups in " + tg.getName () + " thread group: " + agc); tg.list (); } }

ThreadGroupDemoсъздава подходящата група нишки и обекти на нишки, за да отразява това, което виждате на фигурата по-горе. За да докаже, че subgroup 1и subgroup 2групите са mainединствените подгрупи, ThreadGroupDemoправи следното:

  1. Извлича позоваване на основната нишка на ThreadGroupобект чрез обаждане Threadе статичен currentThread()метод (която връща препратка към основната нишка на Threadобект), последвани от Threadлидер ThreadGroup getThreadGroup()метод.
  2. Призовава ThreadGroupе int activeGroupCount()метод за току-що се върна ThreadGroupпрепратката към връщат оценка на активните групи в група конец основната нишка е.
  3. Призовава ThreadGroupе String getName ()метод, за да се върнат име конец група основната нишка е.
  4. Призовава ThreadGroupе void list ()метод за отпечатване на стандартни детайли изходно устройство на група конец основната нишка и всички подгрупи.

Когато се изпълнява, ThreadGroupDemoпоказва следния изход:

Active thread groups in main thread group: 2 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Thread-0,5,main] java.lang.ThreadGroup[name=subgroup 1,maxpri=10] Thread[thread 1,5,subgroup 1] Thread[thread 2,5,subgroup 1] Thread[thread 3,5,subgroup 1] java.lang.ThreadGroup[name=subgroup 2,maxpri=10] Thread[my thread,5,subgroup 2]

Изход, който започва с Threadрезултати от list()вътрешните му разговори с Threadе toString()метод, с мощност формат I, описан в част 1. Заедно с тази продукция, виждате изход, започващ с java.lang.ThreadGroup. Този изход идентифицира името на групата нишки, последвано от нейния максимален приоритет.

Приоритетни и нишки групи

Максималният приоритет на група нишки е най-високият приоритет, който може да постигне всяка от нейните нишки. Помислете за гореспоменатата програма за мрежови сървъри. В рамките на тази програма нишка чака и приема заявки от клиентски програми. Преди да направите това, нишката чакане / приемане на заявка може първо да създаде група нишки с максимален приоритет точно под приоритета на тази нишка. По-късно, когато пристигне заявка, нишката чакане / приемане на заявка създава нова нишка, за да отговори на заявката на клиента и добавя новата нишка към предварително създадената група нишки. Приоритетът на новата нишка автоматично намалява до максимума на групата нишки. По този начин нишката чакане / приемане на заявка реагира по-често на заявки, защото се изпълнява по-често.

Java assigns a maximum priority to each thread group. When you create a group, Java obtains that priority from its parent group. Use ThreadGroup's void setMaxPriority(int priority) method to subsequently set the maximum priority. Any threads that you add to the group after setting its maximum priority cannot have a priority that exceeds the maximum. Any thread with a higher priority automatically lowers when it joins the thread group. However, if you use setMaxPriority(int priority) to lower a group's maximum priority, all threads added to the group prior to that method call keep their original priorities. For example, if you add a priority 8 thread to a maximum priority 9 group, and then lower that group's maximum priority to 7, the priority 8 thread remains at priority 8. At any time, you can determine a thread group's maximum priority by calling ThreadGroup's int getMaxPriority() method. To demonstrate priority and thread groups, I wrote MaxPriorityDemo:

Listing 2. MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg maximum priority = " + tg.getMaxPriority ()); Thread t1 = new Thread (tg, "X"); System.out.println ("t1 priority = " + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("t1 priority after setPriority() = " + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("tg maximum priority after setMaxPriority() = " + tg.getMaxPriority ()); System.out.println ("t1 priority after setMaxPriority() = " + t1.getPriority ()); Thread t2 = new Thread (tg, "Y"); System.out.println ("t2 priority = " + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("t2 priority after setPriority() = " + t2.getPriority ()); } }

When run, MaxPriorityDemo produces the following output:

tg maximum priority = 10 t1 priority = 5 t1 priority after setPriority() = 6 tg maximum priority after setMaxPriority() = 4 t1 priority after setMaxPriority() = 6 t2 priority = 4 t2 priority after setPriority() = 4

Thread group A (which tg references) starts with the highest priority (10) as its maximum. Thread X, whose Thread object t1 references, joins the group and receives 5 as its priority. We change that thread's priority to 6, which succeeds because 6 is less than 10. Subsequently, we call setMaxPriority(int priority) to reduce the group's maximum priority to 4. Although thread X remains at priority 6, a newly-added Y thread receives 4 as its priority. Finally, an attempt to increase thread Y's priority to 5 fails, because 5 is greater than 4.

Note:setMaxPriority(int priority) automatically adjusts the maximum priority of a thread group's subgroups.

In addition to using thread groups to limit thread priority, you can accomplish other tasks by calling various ThreadGroup methods that apply to each group's thread. Methods include void suspend(), void resume(), void stop(), and void interrupt(). Because Sun Microsystems has deprecated the first three methods (they are unsafe), we examine only interrupt().

Interrupt a thread group

ThreadGroupе interrupt()метод позволява нишка да прекъсне нишки и подгрупи специфична група на резба. Тази техника би се оказала подходяща в следния сценарий: Основната нишка на вашето приложение създава множество нишки, всяка от които изпълнява единица работа. Тъй като всички нишки трябва да завършат съответните си работни единици, преди дадена нишка да може да изследва резултатите, всяка нишка изчаква след завършване на работната си единица. Основната нишка следи състоянието на работа. След като всички останали нишки чакат, основната нишка извиква interrupt()да прекъсне чаканията на другите нишки. Тогава тези нишки могат да изследват и обработват резултатите. Листинг 3 показва прекъсване на група нишки:

Листинг 3. InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup { public static void main (String [] args) { MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); try { Thread.sleep (2000); // Wait 2 seconds } catch (InterruptedException e) { } // Interrupt all methods in the same thread group as the main // thread Thread.currentThread ().getThreadGroup ().interrupt (); } } class MyThread extends Thread { public void run () { synchronized ("A") { System.out.println (getName () + " about to wait."); try { "A".wait (); } catch (InterruptedException e) { System.out.println (getName () + " interrupted."); } System.out.println (getName () + " terminating."); } } }