1.1 创建并运行Java线程
行是知之始,知是行之成。
个人认为,学习一个东西应该是先知道其运行起来的效果是什么样的,然后再去深挖其中的原理和需要注意的问题。因此本节立刻让读者体验一下多线程运行起来的效果。
作为一个面向对象的语言,Java中线程也是用一个对象(java.lang.Thread)来表示的。每个进程至少有一个线程,作为程序的入口,通常情况下这个线程我们称之为主线程。在Java中,程序的入口是main方法,因此main方法实际上就是运行在主线程中的。
主线程
我们可以通过以下的代码来进行确认。
public class MainThreadDemo { public static void main(String[] args) { //用于打印主线程的名称 System.out.println(Thread.currentThread().getName()); } }
这段程序的运行结果如下,打印出main,这是主线程的名称,事实上每个线程都有自己的名称。
main |
创建自己的线程
主线程作为程序的入口,因此我们如果需要创建自己的线程,那么必须在主线程中进行创建,也就是在main方法中进行创建。在教程的开头,我们说过一个进程中的多个线程是可以同时运行的。以下的案例在主线程中创建了一个自定义的线程,这样我们的程序中就同时有了两个线程在运行,我们希望通过这段代码来确定多个线程是不是真的可以同时运行:
/** * 演示多个线程可以并发执行的案例 */ public class ThreadDemo { public static void main(String[] args) { //创建一个线程对象,覆盖其run方法,传入参数为线程的名字 Thread t1=new Thread(){ @Override public void run() { for (int i = 1; i <=100 ; i++) { System.out.println("自定义线程循环:"+i+"次"); } } }; //调用start方法启动线程 t1.start(); for (int i = 1; i <=100 ; i++) { System.out.println("主线程循环:"+i+"次"); } } }
为了使代码尽量简单,案例中并没有涉及过多的新的API。读者主要关注的就是我们创建了一个线程Thread
对象,并覆盖了其run
方法,然后调用了其start
方法使其运行。然后我们在main方法和线程的run方法中各自循环了100次,并且打印出一些内容。
以下是笔者在本机上运行以上代码的结果部分内容:
... 自定义线程循环:53次 自定义线程循环:54次 自定义线程循环:55次 自定义线程循环:56次 main方法循环:56次 自定义线程循环:57次 main方法循环:57次 自定义线程循环:58次 main方法循环:58次 自定义线程循环:59次 main方法循环:59次 自定义线程循环:60次 main方法循环:60次 自定义线程循环:61次 main方法循环:61次 自定义线程循环:62次 main方法循环:62次 自定义线程循环:63次 main方法循环:63次 自定义线程循环:64次 main方法循环:64次 自定义线程循环:65次 main方法循环:65次 自定义线程循环:66次 main方法循环:66次 main方法循环:67次 main方法循环:68次 main方法循环:69次 ... |
可以看到main方法中打印的内容和线程run方法中打印的内容有时是交叉运行的,有时又是一个方法循环了好几次才到另一个方法循环(注意,每次的运行结果可能都是不一样的,在后面会讲解原因)。
交叉实现部分说明:
线程run方法和main方法交叉打印出来的内容很好的说明了线程是可以并行运行的。如果不能并行运行,肯定是一个方法执行完,才会让另一个方法执行。
重复打印部分说明:
我们会发现,有的时候run方法或者main方法循环了好几次,才会到另一个方法运行。这是因为线程的运行是抢占式
的。因为有多个线程,为了达到并行执行的效果,CPU需要一会运行这个线程,一会又去运行别的线程,而在不同的线程切换的过程中,CPU并不是按顺序来主义调度这些线程,线程的运行是抢占式
的,意思是一个线程获得一次运行机会之后,下一次能继续请求CPU进行执行。如果CPU连续几次接受了某个线程的执行请求,那么就有可能出现上述情况。
每次运行结果不同说明:
同样还是因为线程的运行是抢占式的原因,我们并不知道程序在每次运行的时候CPU接受到的不同的线程的请求执行的顺序是什么样的。因此对于上述案例,在极端的情况下,可能会出现main方法(或者run方法)先全部打印完,然后再去执行另一个方法的情况。
提示:在调用线程对象的start()方法之前,我们的程序中是只有主线程在运行的。这类似我们运行一个软件,在点击运行图标之前,其只是硬盘上的一个文件而已,只有点击了运行按钮,其才会成为一个进程。对于线程来说是类似的,我们创建了线程对象之后,其仅仅就是Java中的对象而已,只有调用了其start()方法,其才能成为一个真正意义上运行的线程。