并发编程 – 多线程实现

Java多线程实现的三种方式:继承Thread类,实现Runnable接口,实现Callable接口并用FutureTask类包装

进程与线程

实质是资源的轮流抢占,同一时间点上只有一个程序,同一时间段有多个程序并发
线程是进程基础上划分更小的单元,多线程并发处理执行效率高于进程

多线程的实现

多线程均由用户自定义线程类,优先考虑Runnable接口实现,避免单继承写死,从而更好扩充功能;均通过Thread类的start()方法启动线程;通过Callable接口获取线程返回值
若仅调用run()方法而未调用start()方法,相当于普通方法按顺序执行

单继承实现 Thread类实现多线程

  • 定义 定义用户自定义线程类
    public class CustomThread extends Thread{
    //构造方法
    public CustomThread(){}
    //线程主方法
    public void run(){
        //并发运行的内容
    }
    }
  • 使用 使用start()方法启动线程
    public static void main(String[] args){
    new CustomThread().start();
    }

突破单继承局限 Runnable接口实现多线程

Runnable接口定义

@FunctionalInterface  //引入lambda表达式后成为函数式接口
public interface Runnable{
    public void run();
}
  • 定义 定义用户自定义线程类
    class CustomThread implements Runnable{
    //构造方法
    public CustomThread(){}
    //线程主方法
    public void run(){
        //并发运行的内容
    }
    }
  • 使用1 Runnable接口对象传递给Thread类,再由Thread类调用start()方法
    public static void main(String[] args){
    new Thread(new CustomThread()).start();
    }
  • 使用2 lambda表达式定义使用
    Runnable run = ()-> {
    //并发运行的内容
    };
    new Thread(run).start();
  • 使用3 lambda表达式定义使用
    new Thread(()->{
    //并发运行的内容
    }).start();

获取线程返回值 Callable接口实现多线程

Callable接口定义

@FunctionalInterface
public interface Callable<V>{
    public V call() throws Exception;
}

设置一个泛型,此泛型为返回数据的类型,以避免向下转型安全隐患

  • 定义 用户自定义线程类
    class CustomThread implements Callable<T> {
    @Override
    public T call() throws Exception{
        //返回值存入T类型对象逻辑
        return T;//返回T类型对象
    }
    }
  • 使用
    public static void main(String[] args) throws Exception{
    //Callable子类对象传入FutureTask类
    FutureTask<T> task = new FutureTask<>(new CustomThread());
    //FutureTask类对象传入Thread类
    new Thread(task).start();
    //获取返回值
    task.get();
    }

Thread类和Runnable类的关系

Thread类和用户自定义CustomThread类都是Runnable接口的子类,实现时均覆写的是Runnable类的run()方法
实际在使用时,run()方法不能被直接调用,测试类调用的是Thread类的start()方法启动线程,start()方法再去调用CustomThread类的run()方法。Thread类的功能是操作系统线程相关的资源调度处理(源码分析1),用户自定义CustomThread类来实现并发进行的核心业务

  • 源码分析
    Thread类的start()方法调用native start0()抽象方法,由JVM根据不同的操作系统来实现start0()(JNI),从而通过不同的操作系统调用各自对应的资源调度算法

    Thread类构造方法传递Runnable接口对象,接口对象被Thread类对象中的target保存

  • 注意
    每个线程的类对象只允许启动一次,同一个线程重复使用start方法抛出IllegalThreadStateException

    • 使用同一用户自定义线程对象,实质是多个Thread类实例化对象并发访问同一自定义线程资源

      class CustomThread implements Runnable {
      
          private int ticket = 5;
      
          @Override
          public void run() {
              while(ticket > 0)
              System.out.println(ticket--);
          }
      }
      
      public static void main(String[] args) {
          CustomThread thread = new CustomThread();
          new Thread(thread).start();
          new Thread(thread).start();
          new Thread(thread).start();
      }
      > 执行结果 5 4 3 1 2

    • 使用不同自定义线程对象,实质是每个自定义线程对应的Thread类对象访问各自自定义线程中的资源,争抢运行资源并发执行

      class CustomThread implements Runnable {
      
          String name;
          private int ticket = 3;
      
          public CustomThread(String name) {
              this.name = name;
          }
      
          @Override
          public void run() {
              while(ticket > 0)
                  System.out.println(name + ticket--);
          }
      }
      
      public static void main(String[] args) {
          new Thread(new CustomThread("A")).start();
          new Thread(new CustomThread("B")).start();
          new Thread(new CustomThread("C")).start();
      }
      > 执行结果 C3 A3 A2 A1 B3 B2 B1 C2 C1

Callable类和Runnable类的关系

  • 源码分析


    ![](http://47.101.202.111/wp-content/uploads/2018/12/thread-01-1024×521.png)


    继承关系图解

    Future接口

    /* get()方法实现返回值接收 */
    public V get()
    throws InterruptedException,ExecutionException;
    }

    FutureTask接收Callable子类对象,Callable封装在FutureTask中传入Thread类中实现调用

  • Runnable和Callable的区别
    Runnable是JDK1.0提出的,Callable是JDK1.5提出的
    Runnable接口中只有run()方法,无返回值
    Callable接口中只有call()方法,有返回值

线程运行状态


![](http://139.196.114.170/wp-content/uploads/2018/12/thread-02-1.png)

run()方法是线程执行逻辑。Runnable的本质是把一段“无参数、无返回值”的可执行代码封装成一个任务,然后交给不同的执行机制去运行。它本身不代表线程,只代表“任务”。

Thread.start() 本质是启动一个新线程,等待操作系统调用,不阻塞当前线程。新线程被调度时,执行run() 方法。启动后处于就绪态,执行过程中可能存在以下情况:执行一段时间后让出资源进入阻塞状态,直到再次资源调度成功再进入就绪状态。run()方法执行完毕后,线程进入停止状态

“并发编程 – 多线程实现”的一个回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

©2018-2026 Howell版权所有 备案号:冀ICP备19000576号