【Java 多线程并发】 Java中线程的创建和运行

Metadata

title: 【Java 多线程并发】 Java中线程的创建和运行
date: 2023-07-04 22:40
tags:
  - 行动阶段/完成
  - 主题场景/程序
  - 笔记空间/KnowladgeSpace/ProgramSpace/BasicsSpace
  - 细化主题/Java
categories:
  - Java
keywords:
  - Java
description: 【Java 多线程并发】 Java中线程的创建和运行

概述

创建和运行线程的三种方法

  • 继承 Thread 类并且重写 run 方法
  • 实现 Runnable接口的 run 方法
  • 使用 Callable接口和FutureTask类方式

Thread类和Runnable接口的区别

  • Runnable接口可以实现线程之间资源共享,而Thread类不能
  • 实现Runnable接口相对于继承Thread类的优点
    • 适合多个相同程序代码的线程去处理同一资源的情况。
    • 可以避免由于Java的单继承特性带来的局限。
    • 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。
    • 线程池只能放入实现 Runable 或 Callable 类线程,不能直接放入继承 Thread 的类

线程的创建和运行

创建和运行线程的三种方法

Java里的程序天生就是多线程的,那么有几种启动线程的方式?

Java 线程创建有3种方式:

  • 继承 Thread 类并且重写 run 方法
  • 实现 Runnable接口的 run 方法
  • 使用 Callable接口和FutureTask类方式

三者之间的继承关系

由此我们知道了,其实这三种创建方法的根源,都是来源于Runnable接口,这三种方法往上层追溯都能追到Runnble接口。

Thread类和Runnable接口的区别

Runnable接口可以实现线程之间资源共享,而Thread类不能

实际上Thread类和Runnable接口之间在使用上也是有所区别的,如果一个类继承Thread类,就不适合于多个线程共享资源,而实现了Runnable接口,则可以方便的实现资源的共享。

由上文我们就可以知道,Thread类和Runnable接口最大的区别就是继承Thread类不能资源共享,而实现Runnable接口可以资源共享。

为什么Runnable可以共享数据:

总结起来原因就是用Runnable接口的方法可以对两个不同的Thread类的构造方法传入相同的实现Runnable接口的对象,那么这两个不同的Thread线程类本质操控的是同一个Runnable接口的实现对象了,调用的也是同一个run()方法,自然这两个线程下就实现了共享同一个Runnable实现类中的数据了。

如果两个Thread类的构造方法传入不同的Runnable接口实现类,那么两个Thread线程对象操作的不是同一个Runnable实现类,两个线程也就不能共享数据了。

实现Runnable接口相对于继承Thread类的优点

可见,实现Runnable接口相对于继承Thread类来说,有如下显著的优势:

  • 适合多个相同程序代码的线程去处理同一资源的情况。
  • 可以避免由于Java的单继承特性带来的局限。
  • 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。
  • 线程池只能放入实现 Runable 或 Callable 类线程,不能直接放入继承 Thread 的类

实现 Runnable 接口和实现 Callable 接口的区别

  1. Runnable 是自从 java1.1 就有了,而 Callable 是 1.5 之后才加上去的
  2. 实现 Callable 接口的任务线程能返回执行结果,而实现 Runnable 接口的任务线程不能返回结果
  3. Callable 接口的 call()方法允许抛出异常,而 Runnable 接口的 run()方法的异常只能在内部消化,不能继续上抛
  4. 加入线程池运行,Runnable 使用 ExecutorService 的 execute 方法,Callable 使用 submit 方法。注:Callable 接口支持返回执行结果,此时需要调用 FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞

Thread类和Runnable接口关于启动线程的源码解析

实现方法

Java 中实现多线程有两种「基本方式」:继承 Thread 类和实现 Runnable 接口。从实现的编程手法来看,认为这是两种实现方式并无不妥。但是究其实现根源,这么讲其实并不准确。

其实多线程从根本上讲只有一种实现方式,就是实例化 Thread,并且提供其执行的 run 方法。无论你是通过继承 Thread还是实现 Runnable接口,最终都是重写或者实现了 run 方法。而你真正启动线程都是通过实例化 Thread,调用其 start 方法。

来看下两种不同实现方式的例子:

继承 Thread 方式

public class MyThread extends Thread {  
    public void run() {  
        System.out.println("MyThread.run()");  
    }  
}  
MyThread myThread1 = new MyThread();  
MyThread myThread2 = new MyThread();  
myThread1.start();  
myThread2.start();  

实现 Runnable 方式

public class MyThread extends OtherClass implements Runnable {  
    public void run() {  
         System.out.println("MyThread.run()");  
    }  
} 
MyThread myThread = new MyThread();  
Thread thread = new Thread(myThread);  
thread.start(); 

第一种方式中,MyThread 继承了 Thread 类,启动时调用的 start 方法,其实还是他父类 Thread 的 start 方法。并最终触发执行 Student 重写的 run 方法。

第二种方式中,MyThread 实现 Runnable 接口,将MyThread对象作为参数传递给 Thread 构造函数。接下来还是调用了 Thread 的 start 方法。最后则会触发传入的 Runnable 实现类的 run 方法。

两种方式都是创建 Thread 或者 Thread 的子类,通过 Thread 的 start 方法启动。唯一不同是第一种 run 方法实现在 Thread 子类中。第二种则是把 run 方法逻辑转移到 Runnable 的实现类中。线程启动后,第一种方式是 thread 对象运行自己的 run 方法逻辑,第二种方式则是调用 Runnable 实现的 run 方法逻辑。

Thread.start()方法源码分析

  1. 检查线程的状态,是否可以启动;
  2. 把线程加入到线程 group 中;
  3. 调用了 start0 () 方法。

undefined

Runnable.run()方法源码分析

undefined