多线程——认识多线程了解进程与线程的区别掌握Java线程的两种实现方式及其区别了解线程的操作状态本节目标进程与线程DOS系统有一个非常明显的特点,只要一种病毒之后系统会立即死机,因为传统的DOS系统是采用单进程的处理方式,所以只能有一个程序独自运行,其他程序无法运行。Windows系统,即使出现了病毒,系统照样可以正常使用,因为在Windows中采用的是多进程的处理方式,那么在同一个时间段上会有多个程序同时运行。对于WORD来说,每次启动一个WORD之后实际上都是在操作系统上分配了一个进程。线程实际上就是在进程基础之上的进一步划分,从WORD来看,可以把拼写检查当作一个线程进行处理。当然,会同时存在多个线程。如果一个进程没有了,则线程肯定会消失,那么如果线程消失了,但是进程未必会消失。而且,所有的线程都是在进程的基础之上并发同时(同时运行)。进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。多线程是实现并发机制的一种有效手段。进程和线程一样,都是实现并发的一个基本单位。如果现在同时运行多个任务,则所有的系统资源将是共享的,被所以线程所公用,但是程序处理需要CPU,传统的单核CPU来说,在同一个时间段上会有多个程序执行,但是在同一个时间点上只能存在一个程序运行,也就是说,所以的程序都要抢占CPU资源。但是现在的CPU已经发展到多核状态了,在一个电脑上可能会存在多个CPU,那么这个时候就可以非常清楚的发现多线程操作间是如何进行并发执行的。Java的多线程实现在Java中如果要想实现多线程可以采用一些两种方式:继承Thread类实现Runnable接口继承Thread类Thread类是在java.lang包中定义的,一个类只要继承了Thread类,此类就称为多线程操作类。在Thread子类之中,必须明确的覆写Thread类中的run()方法,此方法为线程的主体。多线程的定义语法:class类名称extendsThread{//继承Thread属性…;//类中定义属性方法…;//类中定义方法//覆写Thread类中的run()方法,此方法是线程的主体publicvoidrun(){线程主体;}}java.lang包会在程序运行时自动导入,所以无需手工编写import语句。一个类继承了Thread类之后,那么此类就具备了多线程的操作功能。classMyThreadextendsThread{//继承Thread类,作为线程的实现类privateStringname;//表示线程的名称publicMyThread(Stringname){this.name=name;//通过构造方法配置name属性}publicvoidrun(){//覆写run()方法,作为线程的操作主体for(inti=0;i10;i++){System.out.println(name+运行,i=+i);}}};publicclassThreadDemo01{publicstaticvoidmain(Stringargs[]){MyThreadmt1=newMyThread(线程A);//实例化对象MyThreadmt2=newMyThread(线程B);//实例化对象mt1.run();//调用线程主体mt2.run();//调用线程主体}};线程A运行,i=0线程A运行,i=1线程A运行,i=2线程A运行,i=3线程A运行,i=4线程A运行,i=5线程A运行,i=6线程A运行,i=7线程A运行,i=8线程A运行,i=9线程B运行,i=0线程B运行,i=1线程B运行,i=2线程B运行,i=3线程B运行,i=4线程B运行,i=5线程B运行,i=6线程B运行,i=7线程B运行,i=8线程B运行,i=9观察程序的运行效果:以上的程序是先执行完A之后再执行B,并没有达到所谓的并发执行的效果。因为以上的程序实际上还是按照古老的形式调用的,通过对象.方法,但是如果要想启动一个线程必须使用Thread类中定义的start()方法。一旦调用start(方法),实际上最终调用的就是run()方法。classMyThreadextendsThread{//继承Thread类,作为线程的实现类privateStringname;//表示线程的名称publicMyThread(Stringname){this.name=name;//通过构造方法配置name属性}publicvoidrun(){//覆写run()方法,作为线程的操作主体for(inti=0;i10;i++){System.out.println(name+运行,i=+i);}}};publicclassThreadDemo02{publicstaticvoidmain(Stringargs[]){MyThreadmt1=newMyThread(线程A);//实例化对象MyThreadmt2=newMyThread(线程B);//实例化对象mt1.start();//调用线程主体mt2.start();//调用线程主体}};线程A运行,i=0线程A运行,i=1线程A运行,i=2线程B运行,i=3线程B运行,i=4线程A运行,i=5线程A运行,i=6线程A运行,i=7线程A运行,i=8线程A运行,i=9线程B运行,i=0线程B运行,i=1线程B运行,i=2线程A运行,i=3线程A运行,i=4线程B运行,i=5线程B运行,i=6线程B运行,i=7线程B运行,i=8线程B运行,i=9观察程序的运行效果:从此处的效果看来,确实是并发执行的,哪个线程先抢占到了CPU资源,哪个线程就执行。问题:为什么不直接调用run()方法,而是通过start()调用呢?如果要解决这样的难题,则肯定要打开Thread类的定义,在JDK的src.zip中全部都是Java的源程序代码,直接找到java.lang.Thread类,就可以打开Thread类的定义publicsynchronizedvoidstart(){if(threadStatus!=0)thrownewIllegalThreadStateException();group.add(this);booleanstarted=false;try{start0();started=true;}finally{try{if(!started){group.threadStartFailed(this);}}catch(Throwableignore){}}privatenativevoidstart0();start()方法有可能抛出异常。native关键字表示的是一个由Java调用本机操作系统函数的一个关键字。在Java中,运行Java程序调用本机的操作系统的函数以完成特定的功能。证明,如果现在要是想实现多线程的话,则肯定需要操作系统的支持,因为多线程操作中牵扯到一个抢占CPU的情况,要等到CPU进行调度,那么这一点肯定需要操作系统的底层支持,所以使用了native调用本机的系统函数。而且在各个操作系统中多线程的实现底层代码肯定是不同的,所以使用了native关键字也可以让JVM自动去调用不同的JVM实现。threadStatus也表示一种状态,如果线程已经启动了再调用start()方法的时候就有可能产生异常。classMyThreadextendsThread{//继承Thread类,作为线程的实现类privateStringname;//表示线程的名称publicMyThread(Stringname){this.name=name;//通过构造方法配置name属性}publicvoidrun(){//覆写run()方法,作为线程的操作主体for(inti=0;i10;i++){System.out.println(name+运行,i=+i);}}};publicclassThreadDemo03{publicstaticvoidmain(Stringargs[]){MyThreadmt1=newMyThread(线程A);//实例化对象mt1.start();//调用线程主体mt1.start();//错误}};Exceptioninthreadmainjava.lang.IllegalThreadStateExceptionatjava.lang.Thread.start(Thread.java:682)Runnable接口在Java中也可以通过实现Runnable接口的方式实现多线程,Runnable接口中只定义了一个抽象方法:publicvoidrun();通过Runnable接口实现多线程:class类名称implementsRunnable{//实现Runnable接口属性…;//类中定义属性方法…;//类中定义方法publicvoidrun(){//覆写Runnable接口里的run()方法线程主体;}}classMyThreadimplementsRunnable{//实现Runnable接口,作为线程的实现类privateStringname;//表示线程的名称publicMyThread(Stringname){this.name=name;//通过构造方法配置name属性}publicvoidrun(){//覆写run()方法,作为线程的操作主体for(inti=0;i10;i++){System.out.println(name+运行,i=+i);}}};如果要想启动线程则肯定依靠Thread类,但是之前如果直接继承了Thread类,则可以将start()方法直接继承下来并使用,但是在Runnable接口中并没有此方法,Thread类的构造:publicThread(Runnabletarget)利用以上的构造方法,启动多线程classMyThreadimplementsRunnable{//实现Runnable接口,作为线程的实现类privateStringname;//表示线程的名称publicMyThread(Stringname){this.name=name;//通过构造方法配置name属性}publicvoidrun(){//覆写run()方法,作为线程的操作主体for(inti=0;i10;i++){System.out.println(name+运行,i=+i);}}};publicclassRunnableDemo01{publicstaticvoidmain(Stringargs[]){MyThreadmt1=newMyThread(线程A);//实例化对象MyThreadmt2=newMyThread(线程B);//实例化对象Threadt1=newThread(mt1);//实例化Thread类对象Threadt2=newThread(mt2);//实例化Thread类对象t1.start();//启动多线程t2.start();//启动多线程}}Thread类与Runnable接口Thread定义:publicclassThreadextendsObjectimplementsRunnable从定义格式上可以发现,Thread类也是Runnable接口的子类。Thread类的部分定义从类的关系上来看,之前的做法非常类似于代理设计模式,Thread类完成比线程主体更多的操作,例如:分配CPU资源,判断是否已经启动等等。使用Thread类在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资源共享。classMyThreadextendsThread{//继承Thread类,作为线程的实现类privateintticket=5;//表示一共有5张票publicvoidrun(){//覆写run()方法,作为线程的操作主体for(inti=0;i100;i++){if(this.ticket0){