线程(上)

187 阅读4分钟

线程的基本概念

  • 一个线程是一个程序内部的顺序控制流。
  • 线程和进程
    • 每个进程都有独立的代码和数据空间(进程上下文),进程切换的开销大。
    • 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小
    • 多进程:在操作系统中,能同时运行多个任务(程序)。
    • 多线程:在同一应用程序中,有多个顺序流同时执行。

线程的概念模型

  • 虚拟的CPU,封装在Java.lang.Thread类中。
  • CPU所执行的代码,传递给Thread类。
  • CPU所处理的数据,传递给Thread类。

线程体

  • Java的线程是通过Java.lang.Thread类来现实的。

  • 每个线程都是通过某个特定Thread对象的方法run()来完成其操作的,方法run()称为线程体。

构建线程的两种方法

  • 定义一个线程类,它继承类Thread并重写其中的run()方法。
  class MyThread extends Thread{
      @Override
      public void run(){
          // 业务代码
      }
  }
  
  public class ThreadTester{
      public static void main(String[] args) {
          MyThread thread = new MyThread();
          // 开启线程,将自动进入run()方法;
          thread.start();
      }
  }
  • 提供一个实现接口Runnable的类作为线程的目标对象,在初始化一个Thread类或其子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()。

    • public Thread(ThreadGroup group,Runnable target,String name);
      class MyThread implements Runnable{
          
          public void run(){
              // 业务代码
          }
      }
      
      public class ThreadTester{
          public static void main(String[] args) {
              
              MyThread thread = new MyThread();
              // 开启线程,将自动进入run()方法;
              new Thread(thread).start();
          }
      }
    

两种线程构建方法的比较

  • 使用Runnable接口 可以将CPU,代码和数据分开,形成清晰的模型;还可以继承其他类
  • 直接继承Thread类 编写简单,直接继承,重写run方法,不能在继承其他类

线程的休眠

/**
* 线程休眠的原因:就是让其他线程得到执行的机会
*/
public void run(){
    Thread.sleep(1000); // 休眠1000毫秒
   // 业务代码
}

Thread类-常用API

  • public Thread()
    • 构造一个新的线程对象
  • public Thread(Runnable target)
    • 构造一个新的线程对象,以一个实现Runnable接口的类的对象为参数
  • public Thread(String name)
    • 构造一个新的线程对象,并同时指定线程名
  • public static Thread currentThread()
    • 返回当前正在运行的线程对象
  • public static void yield()
    • 使当前线程对象暂停,允许别的线程开始运行
  • public static void sleep(long millis)
    • 使当前线程暂停运行指定毫秒数,但此线程并不失去以获得的锁
  • public void start()
    • 启动线程,JVM将调用此线程的run()方法,结果是将同时运行两个线程,当前线程和执行run()方法的线程
  • public void run()
    • Thread的子类应该重写此方法,内容应为该线程应执行的任务。
  • public final void stop()
    • 停止线程运行,释放该线程占用的对象锁
  • public void interrupt()
    • 中断此线程
  • public final void join() / join(long millis)
    • 如果此前启动了线程A,调用join方法将等待 (指定毫秒数或) 线程A死亡才能继续执行当前线程
  • public final void setPriority(int newPriority)
    • 设置线程优先级
  • public final void setDaemon(Boolean on)
    • 设置是否为后台线程,如果是当前运行线程均为后台线程则JVM停止运行。这个方法必须在start()方法前使用
  • public final void checkAccess()
    • 判断当前线程是否有权力修改调用此方法线程
  • public void setName(String name)
    • 更改本线程的名称为指定的参数
  • public final boolean isAlive()
    • 测试线程是否处于活动状态,如果线程被启动并且没有死亡则返回true

线程内部的数据共享

/**
* 用三个线程模拟三个售票口,总共售出200张票
* */
public class SellTicketsTester {
    public static void main(String[] args) {
        SellTickets t = new SellTickets();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}

class SellTickets implements Runnable{

    // 总票数(线程共享数据)
    private int tickets = 200;
    public void run() {

        while (tickets > 0){
            System.out.println(Thread.currentThread().getName() + "剩余总数" + --tickets);
        }
    }
}

/** output:
* Thread-0剩余总数199
* Thread-1剩余总数198
* ...
* Thread-0剩余总数3
* Thread-0剩余总数2
* Thread-0剩余总数1
* Thread-0剩余总数0
*/