多线程(二)——线程状态和经常使用方法、守护线程

1、线程状态  

     1-新建状态  ( New ):java

         使用 new 关键字和 Thread 类或其子类创建一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。程序员

         2-就绪状态 ( Runnable ):数据库

         当线程对象调用了start()方法以后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。网络

         3-运行状态 ( Running ):多线程

         若是就绪状态的线程获取 CPU 资源,就能够执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它能够变为阻塞状态、就绪状态和死亡状态。并发

         4-阻塞状态 ( Blocked ):ide

         若是一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源以后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或得到设备资源后能够从新进入就绪状态。能够分为三种:性能

                   等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。this

                   同步阻塞:线程在获取 synchronized 同步锁失败(由于同步锁被其余线程占用)。spa

                   其余阻塞:经过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程从新转入就绪状态。sleep() 不会释放持有的同步锁

         5-死亡状态 (Dead):

         一个运行状态的线程完成任务或者因异常退出了run()方法,该线程就切换到终止状态。死亡状态的线程不能够再执行

线程状态转换图:

          

2、线程调度 

  Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提升程序的执行效率。可是无论程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能作到精准控制

  线程优先级:java中的线程是具备优先级的,优先级高的线程会得到更多的运行机会。

  Java中线程的优先级是用整数来表示的,取值范围是1-10

  Thread类中有三个静态常量:

  static int MAX_PRIORITY

  线程能够具备的最高优先级,取值为10。

  static int MIN_PRIORITY

  线程能够具备的最低优先级,取值为1。

  static int NORM_PRIORITY

       分配给线程的默认优先级,取值为5。

  能够用Thread类的setPriority和getPriority方法分别来设置和获取线程的优先级

  线程的优先级有继承关系,好比A线程中建立了B线程,那么B将和A具备相同的优先级。

  注意:线程优先级不能保证线程执行的顺序,只能说明优先级高的线程更加剧要

3、经常使用方法概括

  一、Thread.sleep(long millis) —— 线程睡眠

   sleep方法是Thread类提供静态方法,表示始终让当前正在执行的线程暂停一段时间,并进入阻塞状态。使用时不要用实例对象调用,只会让当前的线程进入睡眠,而不是使调用该方法的线程对象进入睡眠

   当睡眠的时间结束,线程从新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,咱们不可能精准的控制,因此若是调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。

  二、Thread.yield() —— 线程让步

  yield()方法也是Thread类提供静态方法,它也可让当前正在执行的线程暂停,让出cpu资源给其余的线程。可是和sleep()方法不一样的是,它不会进入到阻塞状态,而是进入到就绪状态yield()方法只是让当前线程暂停一下,从新进入就绪的线程池中,让系统的线程调度器从新调度器从新调度一次,彻底可能出现这样的状况:当某个线程调用yield()方法以后,线程调度器也可能将其调度出来从新进入到运行状态执行

  实际上,当某个线程调用了yield()方法暂停以后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能得到执行的机会,固然,只是有可能,由于咱们不可能精确的干涉cpu调度线程。用法以下:

class MyThread extends Thread { public MyThread(String name, int pro) { super(name);// 设置线程的名称 this.setPriority(pro);// 设置优先级  } @Override public void run() { for (int i = 0; i < 30; i++) { System.out.println(this.getName() + "线程第" + i + "次执行!"); if (i % 5 == 0) Thread.yield(); } } } public class LifecycleThread { public static void main(String[] args) throws InterruptedException { new MyThread("低级", 1).start(); new MyThread("中级", 5).start(); new MyThread("高级", 10).start(); } }

关于sleep()方法和yield()方的区别以下:

①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,因此有可能刚进入就绪状态,又被调度到运行状态。

②、sleep方法声明抛出了InterruptedException,因此调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。

③、sleep方法比yield方法有更好的可移植性,一般不要依靠yield方法来控制并发线程的执行。

  sleep()使当前线程进入停滞状态,因此执行sleep()的线程在指定的时间内确定不会被执行;yield()只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行。

        sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。

  实际上,yield()方法对应了以下操做:先检测当前是否有相同优先级的线程处于同可运行状态,若有,则把 CPU  的占有权交给此线程,不然,继续运行原来的线程。因此yield()方法称为“退让”,它把运行机会让给了同等优先级的其余线程

  另外,sleep 方法容许较低优先级的线程得到运行机会,但 yield()  方法执行时,当前线程仍处在可运行状态,因此,不可能让出较低优先级的线程些时得到 CPU 占有权。在一个运行系统中,若是较高优先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞,那么,较低优先级线程只能等待全部较高优先级的线程运行结束,才有机会运行。 

  三、join —— 等待线程终止

  它不是静态方法,应用场景是当一个线程必须等待另外一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能。

  为何要用join()方法?

 

  在不少状况下,主线程生成并起动了子线程,若是子线程里要进行大量的耗时的运算,主线程每每将于子线程以前结束,可是若是主线程处理完其余的事务后,须要用到子线程的处理结果,也就是主线程须要等待子线程执行完成以后再结束,这个时候就要用到join()方法了。

  不加join代码以下:

class MyThread extends Thread { public MyThread(String name, int pro) { super(name);// 设置线程的名称 this.setPriority(pro);// 设置优先级  } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "" + i + "次执行!"); } } } public class LifecycleThread { public static void main(String[] args) throws InterruptedException { System.out.println("主线程开始"); MyThread t1 = new MyThread("子线程", 10); t1.start(); for (int i = 0; i < 10; i++) { System.out.println("主线程执行代码"); } System.out.println("主线程结束"); } }
主线程开始 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 子线程第0次执行! 子线程第1次执行! 子线程第2次执行! 子线程第3次执行! 主线程结束 子线程第4次执行! 子线程第5次执行! 子线程第6次执行! 子线程第7次执行! 子线程第8次执行! 子线程第9次执行!
输出结果

  这个输出结果只是其中的一种状况,主线程执行不会等待子线程。

  加 join 代码以下:

class MyThread extends Thread { public MyThread(String name, int pro) { super(name);// 设置线程的名称 this.setPriority(pro);// 设置优先级  } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "" + i + "次执行!"); } } } public class LifecycleThread { public static void main(String[] args) throws InterruptedException { System.out.println("主线程开始"); MyThread t1 = new MyThread("子线程", 10); t1.start();  t1.join(); for (int i = 0; i < 10; i++) { System.out.println("主线程执行代码"); } System.out.println("主线程结束"); } }
主线程开始 子线程第0次执行! 子线程第1次执行! 子线程第2次执行! 子线程第3次执行! 子线程第4次执行! 子线程第5次执行! 子线程第6次执行! 子线程第7次执行! 子线程第8次执行! 子线程第9次执行! 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程执行代码 主线程结束
输出结果

  主线程必定会等子线程都结束了才结束。

  

四、interrupt()

  只是向线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,可是若是你吃掉了这个异常,那么这个线程仍是不会中断的!

五、线程类的一些经常使用方法: 

  sleep(): 强迫一个线程睡眠N毫秒。 
  isAlive(): 判断一个线程是否存活。 
  join(): 等待线程终止。 
  activeCount(): 程序中活跃的线程数。 
  enumerate(): 枚举程序中的线程。 
    currentThread(): 获得当前线程。 
  isDaemon(): 一个线程是否为守护线程。 
  setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束) 
  setName(): 为线程设置一个名称。 
  wait(): 强迫一个线程等待。 
  notify(): 通知一个线程继续运行。 
  setPriority(): 设置一个线程的优先级。

4、多线程使用的时机

  多线程的目的是让程序并发执行,改变原有的串行执行方式,来提升效率,当程序中有两个子系统须要并发执行的时候,就须要使用多线程

  多线程是能够编写出高效率的程序,可是必定要注意,太多的线程却会让程序的执行效率实际上下降,由于线程间的切换也是对CPU资源有开销的,过多的线程会使CPU在上下文(线程之间)切换的时间大于程序执行的时间。

5、守护线程

Java线程分两类:

  1.用户线程:运行在前台,执行具体任务(例如:主线程,链接网络的子线程等)。
  2.守护线程:运行在后台,为其余前台线程服务,
  注意:一旦全部用户线程结束运行,守护线程会随JVM一块儿结束工做;最多见守护线程:垃圾回收线程;数据库链接池监测线程;JVM启动的监测线程。

  Ps:如何设置守护线程:能够经过调用Thread类的setDaemon(ture)方法来设置当前的线程为守护线程。

相关文章
相关标签/搜索