java多线程

来源:互联网 发布:vscode ubuntu fali 编辑:程序博客网 时间:2024/05/08 07:36

1、java中的线程

     程序是一段静态的代码,它是应用软件执行的蓝本。进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程。线程是比进程更小的执行单位。一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念。

     Java应用程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法之后,就会启动一个线程,这个线程称作“主线程”,该线程负责执行main方法。那么,在main方法中再创建的线程,就称为主线程中的线程。如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,即main方法返回时,JVM就会结束我们的Java应用程序。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法即使执行完最后的语句,JVM也不会结束我们的程序,JVM一直要等到主线程中的所有线程都结束之后,才结束我们的Java应用程序。

     1>.线程的4种状态
   在Java语言中,Thread类及其子类创建的对象称作线程,新建的线程在它的一个完整的生命周期中通常要经历4种状态,
 (1)新建
 (2)运行
   线程创建后仅仅是占有了内存资源,在JVM管理的线程中还没有这个线程,此线程必须调用start()方法(从父类继承的方法)通知JVM,这样JVM就会知道又有一个新一个线程排队等候切换了。

   当JVM将CUP使用权切换给线程时,如果线程是Thread的子类创建的,该类中的run方法就立刻执行。所以我们必须在子类中重写父类的run方法,Thread类中的run()方法没有具体内容,程序要在Thread类的子类中重写run()方法来覆盖父类的run()方法,run方法规定了该线程的具体使命。
    在线程没有结束run方法之前,不要让线程再调用start方法,否则将发生ILLegalThreadStateException异常。
(3)中断
  有4种原因的中断:
(a) JVM将CPU资源从当前线程切换给其他线程,使本线程让出CPU的使用权处于中断状态。
(b)线程使用CPU资源期间,执行了sleep(int millsecond)方法,使当前线程进入休眠状态。sleep(int millsecond)方法是Thread类中的一个类方法,线程一旦执行了sleep(int millsecond)方法,就立刻让出CPU的使用权,使当前线程处于中断状态。经过参数millsecond指定的豪秒数之后,该线程就重新进到线程队列中排队等待CPU资源,以便从中断处继续运行。
(c)线程使用CPU资源期间,执行了wait()方法,使得当前线程进入等待状态。等待状态的线程不会主动进到线程队列中排队等待CPU资源,必须由其他线程调用notify()方法通知它,使得它重新进到线程队列中排队等待CPU资源,以便从中断处继续运行。有关wait、noftify和notifyAll方法将在第8节详细讨论
 (d)    线程使用CPU资源期间,执行某个操作进入阻塞状态,比如执行读/写操作引起阻塞。进入阻塞状态时线程不能进入排队队列,只有当引起阻塞的原因消除时,线程才重新进到线程队列中排队等待CPU资源,以便从原来中断处开始继续运行。
(4)死亡
   处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有二,一个是正常运行的线程完成了它的全部工作,即执行完run方法中的全部语句,结束了run方法。另一个原因是线程被提前强制性地终止,即强制run方法结束。所谓死亡状态就是线程释放了实体,即释放分配给线程对象的内存。

                   public class Ex

{
 public static void main(String [] args){
 WtiteWordThrea zhang,wang;
 zhang=new WriteWordThread("张小红");
 wang =new WriteWordThread("JamsKeven");
 zhang.start();
 for(int i=1;i<=8;i++){
    System.out.println("我是主线程中的语句");
     }
    wang.start();
  }
}
class WriteWordThread extendds Thread{
   WriteWordThread(String s){
      setName(s);//调用Thread类的setName方法为线程七个名字
   }
   public void run(){
   for(int i=1;i<=8;i++){
   System.out.println("我是一个线程,我的名字是"+getName());
   }
 }
}
2、线程的优先级及调度管理

    Java虚拟机(JVM)中的线程调度器负责管理线程,调度器把线程的优先级分为10个级别,分别用Thread类中的类常量表示。每个Java线程的优先级都在常数1:
    Thread.MIN PRIORITY
    到常数10:
    Thread.MAX_PRIORITY
    的范围内。如果没有明确地设置线程的优先级别,每个线程的优先级都为常数5(包括主线程):
    Thread.NORM_PRIORITY,
    线程的优先级可以通过setPriority(int grade)方法调整,这一方法需要一个int类型参数。如果此参数不在1~10的范围内,那么setPriority便产生一个lllegalArgumenException异常。getPriority方法返回线程的优先级。需要注意是,有些操作系统只能识别3个级别:1,5,10。

3、线程的创建与启动

    创建线程有两种方式:继承java.lang.Threa类和实现Runnable接口。

     1>Thread的子类创建线程

     用户可以扩展 Thread类,但需要重写父类的run方法,其目的是规定线程的具体操作,否则线程就什么也不做,因为父类的run方法中没有任何操作语句。通过调用用户的start()方法即可启动该线程。

     2>实现Runnable接口

      另一种创建线程的方式是实现java.lang.Runnable接口,它只有一个run()方法。当使用Thread(Runnable thread)方式创建线程对象时,需为该方法传递一个实现了Runnable接口的对象,这样创建的线程将调用实现Runnable接口的run()方法。

     如果只是为了实现Thread的执行过程,那么没有必要从Thread中诞生。这是因为实现Runnable接口的对象代表的是一个计算任务,这个计算任务通常交由其他线程去执行的。因此,Runnable对应要完成的任务,Thread对应任务的执行者、另外若一个类已经有了父类,那么只能实现Runnable接口来参与多线程的运行;如果要使用并扩展Thread类的功能,那么可选择从Thread类继承。
 4、Runnable接口与目标对象
   创建线程的另一个途径就是用Thread类直接创建线程对象。使用Thread创建线程对象时,通常使用的构造方法是:
   Thread(Runnable target),
   该构造方法中的参数是一个Runnable类型的接口,因此,在创建线程对象时必须向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称作所创线程的目标对象,当线程调用start方法后,一旦轮到它来享用CPU资源,目标对象就会自动调用接口中的run方法(接口回调),这一过程是自动实现的,用户程序只需要让线程调用start方法即可,也就是说,当线程被调度并转入运行状态时,所执行的就是run()方法中所规定的操作。

5、关于run方法中的局部变量
    对于具有相同目标对象的线程,当其中一个线程享用CPU资源时,目标对象自动调用接口中的run方法,当轮到另一个线程享用CPU资源时,目标对象会再次调用接口中的run方法,。不同线程的run方法中的局部变量互不干扰,一个线程改变了自己的run方法中局部变量的值不会影响其他线程的run方法中的局部变量 

6、     1.start()
    线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期了。
         2.run()
    Thread类的run()方法与Runnable接口中的run()方法的功能和作用相同,都用来定义线程对象被调度之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。

         3.sleep(int millsecond)  
  优先级高的线程可以在它的run()方法中调用sleep方法来使自己放弃处理器资源,休眠一段时间。

         4.isAlive()
    在线程的run方法结束之前,即没有进入死亡状态之前,线程调用isAlive()方法返回true,当线程进入死亡状态后(实体内存被释放),线程仍可以调用方法isAlive(),这时返回的值是false。(线程未调用start方法之前,调用isAlive()方法返回false).
       需要注意的是,一个已经运行的线程在没有进入死亡状态时,不要再给线程分配实体,由于线程只能引用最后分配的实体,先前的实体就会成为“垃圾”,并且不会被垃圾收集机收集掉。

          5.currentThread() 
 currentThread()方法是Thread类中的类方法,可以用类名调用,该方法返回当前正在使用CPU资源的线程。
          6.interrupt()
    intertupt方法经常用来“吵醒”休眠的线程。当一些线程调用sleep方法处于休眠状态时,一个使用 CPU资源的其它线程在执行过程中,可以让休眠的线程分别调用interrupt 方法“吵醒”自己,即导致休眠的线程发生InterruptedException异常,从而结束休眠,重新排队等待CPU资源。

7、线程同步

        线程同步是指多个线程要执行一个synchronized修饰的方法,如果一个线程A占有CPU资源期间,使得synchronized方法被调用执行,那么在该synchronized方法返回之前,即synchronized方法调用执行完毕之前,其他占有CPU资源的线程一旦调用这个synchronized方法就会引起堵塞,堵塞的线程要一直等到堵塞的原因消除( synchronized方法返回),再排队等待CPU资源,以便使用这个同步方法。

       wait()、notify()和notifyAll()都是Object类中的final方法,被所有的类继承,且不允许重写的方法。
         当一个线程使用的同步方法中用到某个变量,而此变量又需要其它线程修改后才能符合本线程的需要,那么可以在同步方法中使用wait()方法。使用wait()方法可以中断方法的执行,使本线程等待,暂时让出CPU的使用权,并允许其它线程使用这个同步方法。其它线程如果在使用这个同步方法时不需要等待,那么它使用完这个同步方法的同时,应当用notifyAll()方法通知所有的由于使用这个同步方法而处于等待的线程结束等待。

8、线程联合

     一个线程A在占有CPU资源期间,可以让其它线程调用join()和本线程联合,
  如:
       B.join();
  我们称A在运行期间联合了B。如果线程A在占有CPU资源期间一旦联合B线程,那么A线程将立刻中断执行,一直等到它联合的线程B执行完毕,A线程再重新排队等待CPU资源,以便恢复执行。如果A准备联合的B线程已经结束,那么B.join()不会产生任何效果。

9、守护线程

     一个线程调用
     void setDaemon(boolean on)
    方法可以将自己设置成一个守护(Daemon)线程,例如:
     thread.setDaemon(true);
    线程默认是非守护线程,非守护线程也称作用户(user)线程。
   当程序中的所有用户线程都已结束运行时,即使守护线程的run方法中还有需要执行的语句,守护线程也立刻结束运行。一般地,用守护线程做一些不是很严格的工作,线程的随时结束不会产生什么不良的后果。一个线程必须在运行之前设置自己是否是守护线程