07.多线程

  1. 进程:进程是程序的一次动态执行过程,它对应了从代码的加载,执行到执行完毕的一个完整过程。

  2. 线程:线程是进程中执行运算的最小单位,可完成一个独立的顺序控制流程,每个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序的运行的入口点,如果在一个进程中同时运行了多个线程,用来完成不同的工作,则成为 “多线程”。

    任何线程一般都具有五种状态:创建、就绪、运行、阻塞、死亡

  3. 多线程的好处:

    1. 充分利用CPU资源
    2. 简化编程模型
    3. 带来良好的用户体验

Java 通过 Thread 类将线程所必须的功能都封装了起来

Thread 类及其常用方法:

构造方法 说明
Thread() 分配新的Thread对象
Thread(Runnable target) 分配新的Thread对象,target为run()方法被调用的对象
Thread(Runnable target,String name) 分配新的Thread对象,target为run()方法被调用的对象,name为新线程的名字
void run() 执行任务操作的方法
void start() 使该线程开始执行,Java虚拟机调用该线程的run()方法
void sleep(loog millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
String getName() 返回线程的名字
int getPriority() 返回线程的优先级
void setPriority(int newPriority) 更改线程的优先级
static Thread currentThread() 返回当前正在执行的线程对象的引用
boolean isAlive() 测试线程是否处于活动状态
void join() 等待该线程终止
void interrupt() 中断线程
void yield() 暂停当前正在执行的线程对象,并执行其他线程

主线程

在Java程序启动时,一个线程立刻运行,该线程通常称为程序的主线程,java程序中的public static void main() 方法是主线程的入口,每个进程都至少有一个主线程。它是程序开始时就执行的。主线程的重要性体现在以下两个方面:

  1. 它是产生其他子线程的线程
  2. 通常它必须最后完成执行,因为它执行各种关闭动作

尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制。为此,需要调用方法 currentThread()获得它的一个引用,currentThread() 方法Thread类的共有静态成员,它的通常形式如下:

static Thread currentThread()

该方法返回一个调用它的线程的引用,一旦获得主线程的引用,就可以像控制其他线程那样控制主线程。


使用一个线程的过程,可以分为四个步骤:

  1. 定义一个线程,同时指明这个线程所要执行的代码
  2. 创建线程对象
  3. 启动线程
  4. 终止线程

在java中创建线程有两个方法:

  1. 继承Thread类

    因为使用此方法创建线程,此线程类需要继承Thread类并重写Thread类的run()方法,因为Thread类的run()方法是线程要执行操作任务的方法,所以线程要执行的操作代码都写run()方法中,并通过调用start()方法启动线程后调用。

    实现步骤:

    1. 定义MyThread类继承Thread类
    2. 重写run()方法,在run()方法中实现数据输出
    3. 创建线程对象,调用start()方法启动线程
  2. 实现Runnable接口

    Runnable接口定义在java.lang包中,其中声明了一个抽象方法run(),一个类可以通过实现Runnable接口并实现其run()方法完成线程的所有活动,已实现的run()方法称为该线程对线的线程体,任何实现runnable接口的对象都可以作为一个线程的目标对象。

    实现步骤:

    1. 定义MyRunnable类实现Runnable接口,并实现run()方法
    2. 创建MyRunnable类型的myRunnable对象
    3. 创建一个Thread类的对象myThread ,将myRunnable对象作为Thread类构造方法的参数传入
    4. 调用myThread对象start方法启动线程

    两种创建线程的方式各有特点和应用领域,直接继承的方式编写简单,可以直接操作线程,适用于间继承的情况;当一个线程继承了另一个类时,就只能通过实现接口的方式来创建线程,而且这种方式还可以实现多个线程之间资源的共享。


    线程的优先级

    每个线程会自动获得一个线程的优先级(Priority),优先级的高低反应线程的重要或紧急程度,一般情况下优先级高的线程获得CPU资源的概率较大

    线程的优先级用1~10表示,1表示最高,默认值为5,这些优先级对应一个Thread类的共用静态常量,例如

    1
    2
    3
    public static final int NORM_PRIORITY=5;
    public static final int MAX_PRIORITY=10;
    public static final int MIN_PRIORITY=1;

    每个线程的优先级介于maxmin之间,线程的优先级可通过getPriority()方法获取,setPriority(int grade)方法更改,grade必须是一个110的整数


    线程的休眠

    在程序中允许一个线程进行暂时休眠,直接使用Thread.sleep(); 方法即可以实现线程的休眠,sleep方法定义语法如下:

    1
    public static void sleep(loog millis);

    sleep方法会让当前线程休眠(停止执行),mills是毫秒,线程运行中的状态进入不可运行状态 ,睡眠时间过后线程会进入可运行状态,调用sleep方法需要处理InterrupteException异常


    线程的强制运行

    join()方法使用当前线程暂停执行,等到调用该方法的线程节后再继续执行本线程,它有三个重载方法:

    1
    2
    3
    public final void join();//第一种
    public final void join(long millis);//第二种,给毫秒
    public final void join(long millis, int nanos);//第三种,给毫秒和纳秒

    join方法也需要处理异常


    线程的礼让

    yield()方法可暂停当前线程执行,允许其他具有相同优先级的线程获得运行机会,该线程任然处于就绪状态,不转为阻塞状态,此时,系统允许其他相同优先级或者更高优先级线程执行,若无其他线程执行,则该线程继续执行

    语法:

    1
    public static void yield();

    线程礼让只是提供一种可能,不能保证一定会实现礼让。


    线程的同步

    一个线程类如果是通过Runnable接口实现的,那么类中的属性又可能被多个该类的线程共享资源,有可能引发线程不安全的问题,解决这中问题需要使用线程同步。

    线程同步有两种方法:同步代码方法和同步代码块

    这两种方法都使用synchronized关键字实现。

    1. 同步方法

      1
      public synchronized void test{};//使用关键字修饰的方法
    2. 同步代码块

      1
      2
      3
      4
      5
      public void test{
      synchronized{//关键字
      //方法体
      }
      }