java的线程组
所有线程都附属于一个线程组。那可以是一个默认线程组,亦但是一个建设线程时明晰指定的组。在建设之初,线程被限制到一个组里,并且不能改变到一个差异的组。每个应用都至少有一个线程从属于系统线程组。若建设多个线程而不指定一个组,它们就会自动归属于系统线程组。
线程组也必需从属于其他线程组。必需在构建器里指定新线程组从属于哪个线程组。若在建设一个线程组的时候没有指定它的归属,则同样会自动成为系统线程组的一名属下。因此,一个应用措施中的所有线程组最终城市将系统线程组作为本身的“父”。
之所以要提出“线程组”的观念,很难从字面上找到原因。这几多为我们接头的主题带来了一些杂乱。一般地说,我们认为是由于“安详”可能“保密”方面的来由才利用线程组的。按照Arnold和Gosling的说法:“线程组中的线程可以修改组内的其他线程,包罗那些位于分层布局最深处的。一个线程不能修改位于本身地址组可能部属组之外的任何线程”(注释①)。然而,我们很难判定“修改”在这儿的详细寄义是什么。下面这个例子展示了位于一个“叶子组”内的线程能修改它地址线程组树的所有线程的优先级,同时还能为这个“树”内的所有线程都挪用一个要领。
①:《The Java Programming Language》第179页。该书由Arnold和Jams Gosling编著,Addison-Wesley于1996年出书
//: TestAccess.java // How threads can access other threads // in a parent thread group public class TestAccess { public static void main(String[] args) { ThreadGroup x = new ThreadGroup("x"), y = new ThreadGroup(x, "y"), z = new ThreadGroup(y, "z"); Thread one = new TestThread1(x, "one"), two = new TestThread2(z, "two"); } } class TestThread1 extends Thread { private int i; TestThread1(ThreadGroup g, String name) { super(g, name); } void f() { i++; // modify this thread System.out.println(getName() + " f()"); } } class TestThread2 extends TestThread1 { TestThread2(ThreadGroup g, String name) { super(g, name); start(); } public void run() { ThreadGroup g = getThreadGroup().getParent().getParent(); g.list(); Thread[] gAll = new Thread[g.activeCount()]; g.enumerate(gAll); for(int i = 0; i < gAll.length; i++) { gAll[i].setPriority(Thread.MIN_PRIORITY); ((TestThread1)gAll[i]).f(); } g.list(); } } ///:~
在main()中,我们建设了几个ThreadGroup(线程组),每个都位于差异的“叶”上:x没有参数,只有它的名字(一个String),所以会自动进入“system”(系统)线程组;y位于x下方,而z位于y下方。留意初始化是凭据文字顺序举办的,所以代码正当。
有两个线程建设之后进入了差异的线程组。个中,TestThread1没有一个run()要领,但有一个f(),用于通知线程以及打印出一些对象,以便我们知道它已被挪用。而TestThread2属于TestThread1的一个子类,它的run()很是详尽,要做很多工作。首先,它获恰当前线程地址的线程组,然后操作getParent()在担任树中向上移动两级(这样做是有原理的,因为我想把TestThread2在分级布局中向下移动两级)。随后,我们挪用要领activeCount(),查询这个线程组以及所有子线程组内有几多个线程,从而建设由指向Thread的句柄组成的一个数组。enumerate()要领将指向所有这些线程的句柄置入数组gAll里。然后在整个数组里遍历,为每个线程都挪用f()要领,同时修改优先级。这样一来,位于一个“叶子”线程组里的线程就修改了位于父线程组的线程。
调试要领list()打印出与一个线程组有关的所有信息,把它们作为尺度输出。在我们对线程组的行为举办观测的时候,这样做是相当有长处的。下面是措施的输出:
java.lang.ThreadGroup[name=x,maxpri=10] Thread[one,5,x] java.lang.ThreadGroup[name=y,maxpri=10] java.lang.ThreadGroup[name=z,maxpri=10] Thread[two,5,z] one f() two f() java.lang.ThreadGroup[name=x,maxpri=10] Thread[one,1,x] java.lang.ThreadGroup[name=y,maxpri=10] java.lang.ThreadGroup[name=z,maxpri=10] Thread[two,1,z]
list()不只打印出ThreadGroup可能Thread的类名,也打印出了线程组的名字以及它的最高优先级。对付线程,则打印出它们的名字,并接上线程优先级以及所属的线程组。留意list()会对线程和线程组举办缩排处理惩罚,指出它们是未缩排的线程组的“子”。
各人可看到f()是由TestThread2的run()要领挪用的,所以很明明,组内的所有线程都是相当懦弱的。然而,我们只能会见那些从本身的system线程组树分支出来的线程,并且或者这就是所谓“安详”的意思。我们不能会见其他任何人的系统线程树。
1. 线程组的节制
抛开安详问题不谈,线程组最有用的一个处所就是节制:只需用单个呼吁即可完成对整个线程组的操纵。下面这个例子演示了这一点,并对线程组内优先级的限制举办了说明。括号内的注释数字便于各人较量输出功效:
//: ThreadGroup1.java // How thread groups control priorities // of the threads inside them. public class ThreadGroup1 { public static void main(String[] args) { // Get the system thread & print its Info: ThreadGroup sys = Thread.currentThread().getThreadGroup(); sys.list(); // (1) // Reduce the system thread group priority: sys.setMaxPriority(Thread.MAX_PRIORITY - 1); // Increase the main thread priority: Thread curr = Thread.currentThread(); curr.setPriority(curr.getPriority() + 1); sys.list(); // (2) // Attempt to set a new group to the max: ThreadGroup g1 = new ThreadGroup("g1"); g1.setMaxPriority(Thread.MAX_PRIORITY); // Attempt to set a new thread to the max: Thread t = new Thread(g1, "A"); t.setPriority(Thread.MAX_PRIORITY); g1.list(); // (3) // Reduce g1's max priority, then attempt // to increase it: g1.setMaxPriority(Thread.MAX_PRIORITY - 2); g1.setMaxPriority(Thread.MAX_PRIORITY); g1.list(); // (4) // Attempt to set a new thread to the max: t = new Thread(g1, "B"); t.setPriority(Thread.MAX_PRIORITY); g1.list(); // (5) // Lower the max priority below the default // thread priority: g1.setMaxPriority(Thread.MIN_PRIORITY + 2); // Look at a new thread's priority before // and after changing it: t = new Thread(g1, "C"); g1.list(); // (6) t.setPriority(t.getPriority() -1); g1.list(); // (7) // Make g2 a child Threadgroup of g1 and // try to increase its priority: ThreadGroup g2 = new ThreadGroup(g1, "g2"); g2.list(); // (8) g2.setMaxPriority(Thread.MAX_PRIORITY); g2.list(); // (9) // Add a bunch of new threads to g2: for (int i = 0; i < 5; i++) new Thread(g2, Integer.toString(i)); // Show information about all threadgroups // and threads: sys.list(); // (10) System.out.println("Starting all threads:"); Thread[] all = new Thread[sys.activeCount()]; sys.enumerate(all); for(int i = 0; i < all.length; i++) if(!all[i].isAlive()) all[i].start(); // Suspends & Stops all threads in // this group and its subgroups: System.out.println("All threads started"); sys.suspend(); // Deprecated in Java 1.2 // Never gets here... System.out.println("All threads suspended"); sys.stop(); // Deprecated in Java 1.2 System.out.println("All threads stopped"); } } ///:~
#p#分页标题#e#
下面的输出功效已举办了适当的编辑,以便用一页可以或许装下(java.lang.已被删去),并且添加了适当的数字,与前面措施列表中括号里的数字对应:
(1) ThreadGroup[name=system,maxpri=10] Thread[main,5,system] (2) ThreadGroup[name=system,maxpri=9] Thread[main,6,system] (3) ThreadGroup[name=g1,maxpri=9] Thread[A,9,g1] (4) ThreadGroup[name=g1,maxpri=8] Thread[A,9,g1] (5) ThreadGroup[name=g1,maxpri=8] Thread[A,9,g1] Thread[B,8,g1] (6) ThreadGroup[name=g1,maxpri=3] Thread[A,9,g1] Thread[B,8,g1] Thread[C,6,g1] (7) ThreadGroup[name=g1,maxpri=3] Thread[A,9,g1] Thread[B,8,g1] Thread[C,3,g1] (8) ThreadGroup[name=g2,maxpri=3] (9) ThreadGroup[name=g2,maxpri=3] (10)ThreadGroup[name=system,maxpri=9] Thread[main,6,system] ThreadGroup[name=g1,maxpri=3] Thread[A,9,g1] Thread[B,8,g1] Thread[C,3,g1] ThreadGroup[name=g2,maxpri=3] Thread[0,6,g2] Thread[1,6,g2] Thread[2,6,g2] Thread[3,6,g2] Thread[4,6,g2] Starting all threads: All threads started
所有措施都至少有一个线程在运行,并且main()采纳的第一项动作即是挪用Thread的一个static(静态)要领,名为currentThread()。从这个线程开始,线程组将被建设,并且会为功效挪用list()。输出如下:
(1) ThreadGroup[name=system,maxpri=10] Thread[main,5,system]
我们可以看到,主线程组的名字是system,而主线程的名字是main,并且它从属于system线程组。
第二个操练显示出system组的最高优先级可以淘汰,并且main线程可以增大本身的优先级:
(2) ThreadGroup[name=system,maxpri=9] Thread[main,6,system]
#p#分页标题#e#
第三个操练建设一个新的线程组,名为g1;它自动从属于system线程组,因为并没有明晰指定它的归属干系。我们在g1内部安排了一个新线程,名为A。随后,我们试着将这个组的最大优先级设到最高的级别,并将A的优先级也设到最高一级。功效如下:
(3) ThreadGroup[name=g1,maxpri=9] Thread[A,9,g1]
可以看出,不行能将线程组的最大优先级设为高于它的父线程组。
第四个操练将g1的最大优先级低落两级,然后试着把它升至Thread.MAX_PRIORITY。功效如下:
(4) ThreadGroup[name=g1,maxpri=8] Thread[A,9,g1]
同样可以看出,提高最大优先级的诡计是失败的。我们只能低落一个线程组的最大优先级,而不能提高它。另外,留意线程A的优先级并未改变,并且它此刻高于线程组的最大优先级。也就是说,线程组最大优先级的变革并不能对现有线程造成影响。
第五个操练试着将一个新线程设为最大优先级。如下所示:
(5) ThreadGroup[name=g1,maxpri=8] Thread[A,9,g1] Thread[B,8,g1]
因此,新线程不能变到比最大线程组优先级还要高的一级。
这个措施的默认线程优先级是6;若新建一个线程,那就是它的默认优先级,并且不会产生变革,除非对优先级举办了出格的处理惩罚。操练六将把线程组的最大优先级降至默认线程优先级以下,看看在这种环境下新建一个线程会产生什么工作:
(6) ThreadGroup[name=g1,maxpri=3] Thread[A,9,g1] Thread[B,8,g1] Thread[C,6,g1]
尽量线程组此刻的最大优先级是3,但仍然用默认优先级6来建设新线程。所以,线程组的最大优先级不会影响默认优先级(事实上,好像没有步伐可以配置新线程的默认优先级)。
改变了优先级后,接下来试试将其低落一级,功效如下:
(7) ThreadGroup[name=g1,maxpri=3] Thread[A,9,g1] Thread[B,8,g1] Thread[C,3,g1]
因此,只有在试图改变优先级的时候,才会强迫遵守线程组最大优先级的限制。
我们在(8)和(9)中举办了雷同的试验。在这里,我们建设了一个新的线程组,名为g2,将其作为g1的一个子组,并改变了它的最大优先级。各人可以看到,g2的优先级无论如何都不行能高于g1:
(8) ThreadGroup[name=g2,maxpri=3] (9) ThreadGroup[name=g2,maxpri=3]
也要留意在g2建设的时候,它会被自动设为g1的线程组最大优先级。
颠末所有这些尝试今后,整个线程组和线程系统城市被打印出来,如下所示:
(10)ThreadGroup[name=system,maxpri=9] Thread[main,6,system] ThreadGroup[name=g1,maxpri=3] Thread[A,9,g1] Thread[B,8,g1] Thread[C,3,g1] ThreadGroup[name=g2,maxpri=3] Thread[0,6,g2] Thread[1,6,g2] Thread[2,6,g2] Thread[3,6,g2] Thread[4,6,g2]
所以由线程组的法则所限,一个子组的最大优先级在任何时候都只能低于或便是它的父组的最大优先级。
本措施的最后一个部门演示了用于整组线程的要领。措施首先遍历整个线程树,并启动每一个尚未启动的线程。譬喻,system组随后会被挂起(暂停),最后被中止(尽量用suspend()和stop()对整个线程组举办操纵看起来好像很有趣,但应留意这些要领在Java 1.2里都是被“阻挡”的)。但在挂起system组的同时,也挂起了main线程,并且整个措施城市封锁。所以永远不会到达让线程中止的那一步。实际上,如果真的中止了main线程,它会“掷”出一个ThreadDeath违例,所以我们凡是不这样做。由于ThreadGroup是从Object担任的,个中包括了wait()要领,所以也能挪用wait(秒数×1000),令措施暂停运行任意秒数的时间。虽然,事前必需在一个同步块里取得工具锁。
ThreadGroup类也提供了suspend()和resume()要领,所以能中止和启动整个线程组和它的所有线程,也能中止和启动它的子组,所有这些只需一个呼吁即可(再次提醒,suspend()和resume()都是Java 1.2所“阻挡”的)。
从外貌看,线程组好像有些让人摸不着脑子,但请留意我们很少需要直接利用它们。