在主方法,也就是main方法中,首先一定存在主线程,也包括在主方法的代码块中有其他线程。下面看一段代码:
import javax.print.attribute.PrintServiceAttribute;
public class TwoThreads {
public static void main(String[] args) {
for(int i = 0;i<10;i++) {
Thread print = new Print();
Thread downloads = new Downloads();
System.out.println("主线程开始");
print.start();
downloads.start();
/*
* try { Thread.sleep(1000); }catch (InterruptedException e) { // TODO: handle
* exception e.printStackTrace(); }
*/
}
}
}
class Print extends Thread{
public void run() {
System.out.println("print is running");
}
}
class Downloads extends Thread{
public void run() {
System.out.println("downlad is running");
}
}
我们可以看到在主方法中有print线程和downloads线程(注意,此处并未给线程明明,只是为了方便称呼,用对象名字代替线程名字,如果需要给线程命名。需要使用setName()方法,比如print.setName("print")或者直接利用Thread的构造方法进行命名,例如Thread thread = new Thread("name"))。运行结果如下(在你的环境中运行结果可能不一样):
主线程开始
主线程开始
downlad is running
print is running
主线程开始
print is running
downlad is running
主线程开始
print is running
downlad is running
主线程开始
print is running
downlad is running
print is running
主线程开始
downlad is running
print is running
主线程开始
downlad is running
print is running
downlad is running
主线程开始
print is running
主线程开始
downlad is running
print is running
downlad is running
主线程开始
print is running
downlad is running
为什么会这样呢?首先,我们需要明确,虽然线程是否进入RUNNABLE状态不由程序员控制,但是线程内部的代码块是按顺序执行的,比如主线程中,先进入fo的第一次r循环,再new 一个print对象,然后再new一个downloads对象,然后再打印“主线程开始”(注意:打印这个部分并不是主线程真正开始的地方,可以近似认为在运行程序的的同时,主线程已经start(),主线程开始执行的时间一定先于在main方法内部new出来的线程),然后print进入可执行状态,也就是print.start(),接着,downloads进入可执行状态,进入可执行状态并不是立即开始运行,而是由CPU自行调度,线程何时进入执行状态(RUNNABLE)与程序员无关,这里一次for循环已经结束,然后主线程进行第二次for循环,依此类推。
为什么会连续打印出两个主线程呢?因为如果不进行其他操作(如sleep(),join())主线程并不是等其他线程结束才结束,而是根据自己是否占有CPU资源进行执行,可以看作是这三条线程独立,但是其他线程必须首先依赖主线程进入可执行状态(start())。这下就明白了为什么连续两次打印主线程,就是因为主线程抢到了CPU资源,所以连续打印两次,但是这时第一次for循环中的两个线程并未抢到CPU资源,并未执行,所以再第二次打印后主线程那句话后才进行。
那么那些new出来的线程什么时候执行呢?不知道,此一次打印出来的“print is running”并不一定是第一次new出来的线程,downloads同理。唯一可以确定的是,再某次循环new出来的线程一定再该次循环打印出来的“主线程开始”之后再执行。这new出来的20条线程什么时候执行是随意的。不妨给每次循环new出来的print和downloads 命名为setName("print"+i)和setName("downloads"+i),再利用Thread.currentThread.getName()在run方法中打印出线程名称,看看打印顺序是怎样的。
那么在for循环下那段打印主线程的那句话(总共打印了10次)会不会乱序执行呢?显然不会,原因在上述已经说过,主线程的代码块中的语句是顺序执行的,不妨将那段打印主线程的语句改为如下语句,看看会发生什么:
System.out.println("主线程开始"+i);
你会发现打印出来的i一定是从0到9,中间不会乱序。
主线程并不一定在其他线程都执行完成后才执行结束,如果不进行其他操作,主线程何时结束并不取决于你,不妨在主方法的for循环语句之后加上如下语句,看看这段语句会何时打印出来:
System.out.println("主线程结束");
你会发现这段语句一定在"主线程开始9"之后打印,但并不一定在最后打印。