过程与线程

  • 过程是程序向操作系统申请资源(如内存空间和文件句柄)的根本单位.
  • 线程是过程中可独立执行的最小单位.

JAVA线程API

  1. 在Java中创立一个线程就是创立一个Thread类的实例。
  2. 每个线程都有其要执行的工作.线程工作的解决逻辑是在Thread类的run办法中实现或进行调用的,因而run办法相当于线程工作逻辑解决的入口办法,它由java虚拟机在运行相应的线程时进行调用,而不是由利用代码进行调用。
  3. Thread类的start办法是用于启动相干线程的,启动一个线程的本质是申请java虚拟机运行相应的线程,而这个线程具体什么时候运行是由线程调度器决定的.因而,尽管start办法被执行了,然而并不意味着这个线程就开始运行了,它可能稍后运行,也可能永远不运行.
  4. Thread中罕用的两个结构器是:Thread()和Thread(Runnable target),这两种创立线程的形式如下:

    1. 定义Thread类子类的形式创立线程

      public class WelcomeApp {  public static void main(String[] args) {    // 创立线程    Thread welcomeThread = new WelcomeThread();    // 启动线程    welcomeThread.start();    // 输入“以后线程”的线程名称    System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName());  }}// 定义Thread类的子类class WelcomeThread extends Thread {  // 在该办法中实现线程的工作解决逻辑  @Override  public void run() {    System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName());  }}
    2. 实现Runnable接口的形式创立线程

      public class WelcomeApp1 {  public static void main(String[] args) {    // 创立线程    Thread welcomeThread = new Thread(new WelcomeTask());    // 启动线程    welcomeThread.start();    // 输入“以后线程”的线程名称    System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName());  }}class WelcomeTask implements Runnable {  // 在该办法中实现线程的工作解决逻辑  @Override  public void run() {    // 输入“以后线程”的线程名称    System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName());  }}
  5. 不论应用以上哪种形式运行,一旦线程的run办法执行完结,相应线程的运行也就完结了,运行完结的线程所占的资源会如同其它java对象一样被java虚拟机垃圾回收。
  6. Thread的实例只能start一次,若屡次调用一个实例的start()会抛出IllegalThreadStateException异样。
  7. 能够通过Thread.currentThread()获取以后线程,进而能够对其进行属性设置或获取它的相干信息,例如:

    Thread.currentThread().setName("线程A");Thread.currentThread().getName();
  8. 上述中,线程的run办法个别由java虚拟机调用,然而,线程也是一个Thread类的一个实例其次run办法也是由public修饰符润饰,所以run办法也能被间接调用,然而个别不会这么做,违反咱们床架线程的初衷。

    public class WelcomeApp {  public static void main(String[] args) {    // 创立线程    Thread welcomeThread = new WelcomeThread();    // 启动线程    welcomeThread.start();    // 输入“以后线程”的线程名称    System.out.printf("1.Welcome! I'm %s.%n", Thread.currentThread().getName());    welcomeThread.run();  }}// 定义Thread类的子类class WelcomeThread extends Thread {  // 在该办法中实现线程的工作解决逻辑  @Override  public void run() {    System.out.printf("2.Welcome! I'm %s.%n", Thread.currentThread().getName());  }}==================后果==================1.Welcome! I'm main.2.Welcome! I'm Thread-0.2.Welcome! I'm main.
  9. 线程的属性

    属性属性类型及用处只读重要注意事项
    编号
    ID
    用于标识不同的线程,不同的线程领有不同的编号某个编号的线程运行完结后,该编号可能被后续创立的线程应用。不同线程领有的编号尽管不同,然而这种编号的唯一性只在Java虚拟机的一次运行无效。也就是说重启一个Java虚拟机(如重启Web服务器)后,某些线程的编号可能与上次Java虚拟机运行的某个线程的编号一样,因而该属性的值不适宜用作某种惟一标识,特地是作为数据库中的惟一标识(如主键)
    名称
    Name
    用于辨别不同的线程。默认值与线程的编号无关,默认值的格局为:Thread-线程编号,如Thread-0Java并不禁止咱们将不同的线程的名称属性设置为雷同的值。尽管如此,设置线程的名称属性有助于代码调试和问题定位
    线程类别
    Daemon
    值为true示意相应的线程为守护线程,否则示意相应的线程为用户线程。该属性的默认值与相应线程的父线程的该属性的值雷同,在失常进行java程序时,当有用户线程还没执行完虚拟机不会立刻进行,会期待其执行结束,然而如果只有守护线程还没执行完则不会阻止虚拟机进行,这阐明守护线程通常用于执行一些重要性不是很高的工作,例如监督其它线程的该属性必须在相应线程启动之前设置,即对setDaemon办法的调用必须在对start办法的调用之前,否则setDaemon办法会抛出IllegalThreadStateException异样。负责一些要害工作解决的线程不合适设置为守护线程
    优先级
    Priority
    该属性实质上是给线程调度器的提醒,用于示意应用程序心愿哪个线程可能优先得以运行。Java定义了1~10的10个优先级。默认值个别为5。对于具体的一个线程而言,其优先级的默认值与其父线程(创立该线程的线程)的优先级值相等个别应用默认优先级即可。不恰当地设置该属性值可能导致重大的问题(线程饥饿)
  10. 线程办法

    办法性能备注
    static Thread
    currentThread()
    返回以后线程,即以后代码的执行线程(对象)同一段代码对Thread.currentThread()的调用,其返回值可能对应着不同的线程(对象)
    void run()用于实现线程的工作解决逻辑该办法是由Java虚拟机间接调用的,个别状况下应用程序不应该调用该办法
    void start()启动相应线程该办法的返回并不代表相应的线程曾经被启动。一个Thread实例的start办法只可能被调用一次,屡次调用会导致异样的抛出
    void join()期待相应线程运行完结若线程A调用线程B的join办法,那么线程A的运行会被暂停,直到线程B运行完结
    static void yield()使以后线程被动放弃其对处理器的占用,这可能导致以后线程被暂停这个办法是不牢靠的。该办法被调用时以后线程可能依然持续运行(视零碎以后的运行状况而定)
    static void sleep(long millis)使以后线程休眠(暂停运行)指定的工夫

Thread和Runnable的关系

  1. Thread类实现Runnable接口

    public class Thread implements Runnable{}
  2. Runnable是一个interface且其中只有一个办法,所以实现Runnable的类要从新run办法。
  3. 两种形式执行工作逻辑的过程

    • 实现Runnable接口,重写run办法

      class WelcomeTask implements Runnable {  @Override  public void run() {    //工作逻辑   }}
      new Thread(new WelcomeTask()).start();

      WelcomeTask实例在Thread中的传递过程,它最终会被赋值给Thread中的一个target成员变量

      private Runnable target;

      具体过程如下:
      1.有参构造函数,生成一个ThreadName,例如:Thread-0

      public Thread(Runnable target) {    init(null, target, "Thread-" + nextThreadNum(), 0);}

      2.进入第一个初始化函数,这外面无需关注其它参数,因为还有其它构造方法会调用init(...)

      private void init(ThreadGroup g, Runnable target, String name,long stackSize){    init(g, target, name, stackSize, null, true);}

      3.第二层初始化函数,行将target赋值

      private void init(ThreadGroup g, Runnable target, String name,                  long stackSize, AccessControlContext acc,                  boolean inheritThreadLocals) {    if (name == null) {        throw new NullPointerException("name cannot be null");    }    this.name = name;    .....    this.target = target;    ....}

      4.Thread实例调用start()办法

      public synchronized void start() {    //threadStatus != 0代表这个Thread实例曾经被start()过了,所以会抛出异样    if (threadStatus != 0)        throw new IllegalThreadStateException();    ...    try {        //最终会调用本地办法start0()来启动这个线程        start0();        ...    } finally {        ...    }}

      5.本地办法启动线程,行将线程交给虚拟机的线程调度器

      private native void start0();

      6.线程调度器执行线程工作逻辑

      //启动线程后,调度器会帮忙咱们运行执行线程run()办法,即工作逻辑//如果target != null的时候会调用WelcomeTask中实现的工作逻辑,否则什么都不会执行public void run() {    if (target != null) {        target.run();    }}
    • 继承Thread类,重写run办法

      class WelcomeThread extends Thread {     @Override     public void run() {       //工作逻辑     }}    
      new WelcomeThread().start();...//任务调度器间接调用WelcomeThread中实现的工作逻辑
  4. 两种形式的区别

    • 面向对象角度:一种是基于继承实现extends Thread,一种是基于组合new Thread(new Runable()),组合绝对与继承来说耦合性更低。
    • 对象共享的角度:一个CountingTask实例能够被多个线程共享所以可能呈现资源竞争问题。

      class CountingThread extends Thread {    int count = 0;    @Override    public void run() {        for (int i = 0; i < 100; i++) {            i++;        }    }    System.out.println("count:" + count);}class CountingTask implments Runnable {    int count = 0;    @Override    public void run() {        for (int i = 0; i < 100; i++) {            i++;        }    }    System.out.println("count:" + count);}public static void main(String[] args) {    Thread thread;    CountingTask task = new CountingTask();    for(int i = 0;i < 10;i++){        thread = new Thread(task);        thread.start();    }    for(int i = 0;i < 10;i++){        thread = new CountingThread();        thread.start();    }}

线程的生命周期

  1. New(新创建)
    Thread thread = new Thread();
    此时线程只是备new进去并没有开始执行。
  2. Runnable(可运行)
    thread.start();
    1.ready:还没有被分配资源 。
    2.running:正在执行 。
  3. 阻塞
    1.Blocked(阻塞) 没有获取被synchronized爱护的代码的锁 。
    2.Waiting(期待)

     1.Object.wait();      - o.notify()/o.notifyAll() 2.Thread.join();      - join线程完结/被中断  Blocked与Waiting的区别是Blocked是在期待开释某个资源,Waiting是在期待达到某个条件 。
  4. Time Waiting(计时期待) 设置了工夫参数的一些阻塞 。
    1.Thread.sleep(m);

      - 工夫超时/join的过程完结/被中断 。

    2.Object.wait(m)

     - 工夫到/o.notify()/o.notifyAll() 

    3.Thread.join(m)

     - 工夫超时/join的过程完结/被中断 。
  5. Terminated(终止)
    1.run办法失常执行结束 。
    2.呈现没有捕捉的异样,意外终止。