//PrintThread.java public class PrintThread extends Thread//继承Tread类 private int count=0 //定义一个count变量用于统计打印的次数并共享变量 public static void mainString args//main方法开始 PrintThread p=new PrintThread//创建一个线程实例 p.start//执行线程 for{;;}//主线程main方法执行一个循环,for执行一个死循环 count++ System.out.printcount+″:Main\n″//主线程中打印count +“main”变量的值,并换行 public void run//线程类必须有的run()方法 for{;;} count++ System.out.printcount+″:Thread\n″ |
上面这段程序便是继承java.lang.Tread并覆盖run的方法。用Java 虚拟机启动程序时,这个程序会先生成一个线程并调用程序主类的main方法。这个程序中的main方法生成新线程,连接打印“Thread”。在启动线程之后,主线程继续打印“Main”。
编译并执行这个程序,然后立即按“Ctrl+C”键中断程序,你会看到上面所述的两个线程不断打印出:XXX:main…..XXX:Thread…. XXX代表的是数字,也就是上面count的值。在笔者的机器上,不同时刻这两个线程打印的次数不一样,先打印20个main(也就是先执行20次主线程)再打印出50次Thread,然后再打印main……
提示:为了便于查看该程序的执行结果,你可以将执行结果导入一个文本文件,然后打开这个文件查看各线程执行的情况。如运行:
javac PrintThread.java
Java PrintThread>1.txt
第一个命令javac PrintThread.java是编译java程序,第二个是执行该程序并将结果导入1.txt文件。这样,打开这个文件,你就可以看见详细的结果了(注意:程序的执行时间不能太长,不然生成的1.txt文件会很庞大)。当然你可以直接执行命令:java PrintThread。
实现java.lang.Runnable接口
运行线程的另一种方法是实现Runnable接口,然后生成运行这个类的线程即可。Runnable接口是定义在java.lang包中的一个接口,其中只提供了一个抽象的run声明。
下面我们来看看如何实现Runnable接口,而不是扩展Thread类。
//PrintRunnableThread.java 实现Runnable接口 public class PrintRunnableThread implements Runnable public static void mainString args Thread t=new Threadnew PrintRun nableThread //t.setPriorityThread.MAX_PRIORI TY//设置最大优先级 t.start//线程开始 for{;;}//不停地打印字符M,代表主线程main System.out.println″M″ public void run for{;;}//不停地打印字符T,代表线程thread System.out.println″T″ |
//ThreadGroupTest.java public class ThreadGroupTest implements Runnabl e public void run public static void main(String args[]) //生成一个新的线程组,并将两个线程对象放到该线程组里。 ThreadGroup threadgroup=new ThreadGroup″线程组″ Thread t1=new Threadthreadgroupnew ThreadGrou pTest″线程 1″ Thread t2=new Threadthreadgroupnew ThreadGrou pTest″线程 2″ //找到顶级的父线程 ThreadGroup parent=Thread.currentThread.getThrea dGroup//得到当前线程的线程组 whileparent.getParent=null parent=parent.getParent//得到父线程 //list方法打印出当前线程组的所有内容线程和子线程组 parent.list |
线程优先级
虽然我们说线程是并发运行的。然而事实常常并非如此。当系统中只有一个CPU时,以某种顺序在单CPU情况下执行多线程被称为调度scheduling。Java采用的是一种简单、固定的调度法,即固定优先级调度。这种算法是根据处于可运行线程的相对优先级来实行的。当线程产生时,它继承原线程的优先级。在需要时可对优先级进行修改。在任何时刻,如果有多条线程等待运行 系统选择优先级最高的可运行线程运行。只有当它停止、自动放弃、或由于某种原因成为非运行状态优先级的线程时才能运行。如果两个线程具有相同的优先级它们将被交替地运行。
Java中的第一个线程都有优先级,线程的优先级是介于Thead.MIN_PRIORITY到Thread.MAX_PRIORITY之间的整数介于0到10之间。缺省情况下,线程的优先级是5即NORM_PRIORITY。我们可以用形如Thread.setPriorityThread.MIN_PRIORITY这样的表达式来设置线程的优先级稍后会在例程中用到。也可以通过getPriority来得到线程的优先级,还可以通过setPriority在线程创建之后的任意时间改变线程的优先级。
提示:当线程中的代码创建一个新线程对象时,这个新线程拥有与创建它的线程一样的优先级。
线程的管理
单线程的程序都有一个main执行体,它运行一些代码,当程序结束执行后,程序结束运行。在Java中我们要得到相同的应答,必须稍微进行改动。只有当所有的线程退出后,程序才能结束。只要有一个线程一直在运行,程序就无法退出。
线程包括new()开始、running()运行、wait()等候和done结束执行状态。第一次创建线程时,都位于new状态,在这个状态下,不能运行线程,只能等待。这时,线程要么调用start方法开始运行,要么送往done状态结束。位于done中的线程已经结束执行,这是线程的最后一个状态。一旦线程位于这个状态,就不能再次出现,而且当Java虚拟机中的所有线程都位于done状态时,程序就强行中止。
当前正在执行的所有线程都位于running状态,在程序之间用某种方法把处理器的执行时间分成时间片。位于running状态的每个线程都是能运行的,但在一个给定的时间内,每个系统处理器只能运行一个线程。与位于running状态的线程不同,由于某种原因,可以把已经位于waiting状态的线程从一组可执行线程中删除。如果线程的执行被中断,就回到waiting状态。这时,线程可能被挂起,在系统资源上等候,或者被告知进入休眠状态Sleep。该状态的线程可以返回到running状态,也能由stop送入done状态。Thread类提供了Sleep()、Stop、Yield()、Suspend()和Resume()等方法来管理线程。
线程操作的其它概念
通过这期两个程序的学习,可能你会认为Java的线程操作很简单,但实事并非如此。在实际工作中,可能需要综合考虑很多问题,比如设置监控线程、暂停、命名和协调线程、设置线程的优先级、共享变量、线程同步、线程池、线程组等。对于这些线程相关的操作,笔者不在本章中详细讲述了。如果读者有兴趣,可参考相应的专著,毕竟这部分不是一两篇文章可以完全讲解完的。