黑马程序员_多线程(概念和创建)

来源:互联网 发布:mac便签 桌面显示 编辑:程序博客网 时间:2024/05/23 20:42

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 

多线程(概念和创建)

一、多线程概念

提到线程,就得提到进程,进程就是正在进行中的程序。但是进程并不是多个程序同步执行他们,而是cpu逐个执行,先执行一个程序,然后执行另一个程序,cpu这是其实是在进行快速切换,只是速度很快,我们看起来像是在同步执行。

线程进程里的东西,理解的来说,比如迅雷中下载一个软件,这个软件通常看到的不是一个整体下载的,而是分成很多压缩包同时在下载,这多个压缩包就会同时向服务端请求数据,这就是线程,整个软件就是进程。这样看来,一个进程里至少含有一个线程。

我们在运行java程序的时候,JVN也开启了一个javaexe的进程,程序中有主函数main,这个主函数中就含有一个线程,我们称之为主线程。之前,所学习的程序,都一直是单线程形式的。

多线程,可以使多个代码同时执行,提高了效率。

总结:

1、             进程是一个正在执行中的程序。每个进程都有一个执行顺序,该顺序是一个执行路径或者控制单元。

2、             线程是进程中的一个独立控制单元。线程在控制着进程的执行。

3、             一个线程至少有一个线程。

4、             多线程可以提高效率。

 

二、             创建多线程

创建多线程,现在就是在我们自己写的程序中加入多线程。

创建方式1

         步骤:1、创建一个类,继承Thread

                     2、复写Thread中的run方法

          3、建立子类对象,调用线程的start方法,该方法有两个作用,一是启动该线程,二是调用run方法。

以我自己的理解来描述:像异常中的自定义异常类,在多线程中,想要在主函数(我们现在常写的程序)中建立一个线程,首先要定义一类继承Thread,这个类必须复写run方法,这两步是必须的。这时线程还没有建立,我们需要在主函数中建立自定义类的对象,通过对象调用start方法,格式为:对象名.start()。此处start方法,建立了一个新的线程,同时调用run方法,这就是我们要复写run方法的原因。

看一下示例程序:

publicclass TestOfJava {

    publicstaticvoid main(String[] args) {

       Demo d = new Demo();

       d.start();

       for(int x=0;x<60;x++){

           System.out.println("mainrun"+x);

       }

    }

}

class Demoextends Thread{

    publicvoid run(){

       for(int x=0;x<60;x++){

           System.out.println("Demorun"+x);

       }

      }

}

看上述程序,就是在主函数中开辟了一个新的线程,start开启新线程,同时调用了run方法。在主函数中,其实有两个线程,一个是主函数的线程,可以称之为主线程。现在又加入一个新线程,此时,这个javaexe进程中就有两个线程了。这两个线程不是同步进行的,可以看上述程序结果,是一会儿执行主线程,一会儿执行自定义线程。而且每次运行结果不一样。因为多个线程都在获取cpu执行权,cpu执行到谁,谁就运行。需要明确的一点是,在某个时刻,只能有一个程序在运行(仅限单cpu情况)。CPU实际上是在做快速切换动作,因为以毫秒计时,所以看上去是同时运行的效果。我们可以形象的把多线程的运行i型能够为看成在互相抢夺CPU执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行时间多长,cpu来决定。

 

具体为什么要覆盖run方法?

Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该代码功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。

我们创建一个新的线程,就是为了执行我们定义的代码,而start是开启新线程,当我们不覆盖run方法,Thread中的run方法中什么代码都没有,所以不覆盖run方法没有意义;当我们覆盖run方法,在run方法中加入我们要运行的代码,这样新建线程就可以执行我们自己定义的代码。

线程中常用方法:

static Thread currentThread():获取当前线程对象

getName():获取线程名称

setName():设置线程名称。注意:可以再子类中覆盖setName方法,或是在子类构造函数中调用父类构造函数即可。

 

创建线程总结:

1、  首先自定义线程要继承Thread类,使得其加入java线程体系。

2、  覆盖run方法,在run方法中加入我们要运行的程序。

run方法中,就是要存储我们想要运行的代码,就这个作用。

3、  建立自定义线程类的对象,调用start方法。

我们要在主函数中开启新线程,只有start方法能开启。如果直接调用run或者其他方法,其实这时就没有开启新线程,只是把我们定义的代码执行一遍,其实还是只有一个主线程。

4、  cpu功能,同一时间只能执行一个程序

5、  多线程的特性就是随机性。

 

三、线程的五种运行状态

语言描述来说,一个线程继承Thread和复写run方法,然后荣国start方法建立了新线程,创建线程就是一个状态。建立线程后,我们自定义的程序就会运行,运行也是一个状态。当线程运行时,cpu在某一时刻只执行一个程序,那么其他程序就会等待,这就分了两种情况,一是没有运行资格,二是有有运行资格但是没有执行权,前一种称为冻结,简单来说就是放弃了执行资格,这其中也有两种状态,一是sleeptime),就是让程序睡一定的时间,时间到了就会自动唤醒,使得程序具备运行资格;二是wait(),就是让程序等待,只有通过notify()来主动唤醒程序,使得程序有运行资格。有运行资格的程序,也会有两个方向,一是直接运行,二是因为cpu某一时间段只运行一个程序,这些有运行资格但是还没有轮到执行权的程序,就会在另一种状态,称为阻塞状态。最后一种,就是线程执行完毕,就会消亡,消亡也是一状态。看图例:

 

 


四、创建线程的第二种方法

方法步骤为

1、  定义类实现Runnable接口。注意和原来继承Thread不同。

2、  覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中。接口中有一个run方法,我们定义的类中也要吧run方法复写。

3、  通过Thread类建立线程对象。Runnable并不能实现Thread类的功能,只有Thread类的功能才能建立线程。

4、  Runnable接口的子类对象,最为实际参数传递给Thread类的构造函数。为什么呢?因为,自定义的run方法所属的对象时Runnable接口的子类对象,所哟要让线程去指定指定对象的run方法,就必须明确该run方法所属的对象。

5、  调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

示例程序如下:

classTicket implements Runnable//extendsThread {

         public void run(){

         }

}

classTestForYou {

         public static void main(String[] args){

         Ticket t  = new Ticket();

         Threadt1 = new Thread(t);

         t1.start();

}

需要强调的是,一、Runnable接口的子类,没有start方法,不能建立线程而如果光有Thread建立的对象,线程是可以建立的,但是运行的run方法时Thread本身的,对于我们来说没有意义。二、只有Thread的类和其子类可以建立线程,所以才把Runnable接口的子类对象传入,是Thread线程建立时,可以调用我们要运行的run方法。

 

 

既然有两个线程建立方式,那么总结一下

1)  一个是实现方式,一个是继承方式。

2)  实现方式避免了单继承的局限性。因为编程中,我们定义的子类可能会因为需要已经继承了另一个父类,而子类中也有我们需要的同步执行的代码,那么java中单继承规则的存在,使得不能使用直接继承Thread类的方式创建新线程。这就用到了实现方式,实现Runnable接口,间接的增加的程序的拓展性。

3)  在使用上,一般建立线程使用实现方式,因为更有拓展性,接口也可多实现。

4)  注意两者的区别,继承方法中,线程代码存放于Thread子类的run方法中;实现方法中,线程代码存放于Runnable接口子类的run方法中。

5)   

0 0
原创粉丝点击