【Java 多线程并发】 Java中线程的创建和运行
【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 接口的区别
- Runnable 是自从 java1.1 就有了,而 Callable 是 1.5 之后才加上去的
- 实现 Callable 接口的任务线程能返回执行结果,而实现 Runnable 接口的任务线程不能返回结果
- Callable 接口的 call()方法允许抛出异常,而 Runnable 接口的 run()方法的异常只能在内部消化,不能继续上抛
- 加入线程池运行,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()方法源码分析
- 检查线程的状态,是否可以启动;
- 把线程加入到线程 group 中;
- 调用了 start0 () 方法。
undefined
Runnable.run()方法源码分析
undefined