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类的关系
-
源码分析

继承关系图解
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()方法,有返回值
线程运行状态

run()方法是线程执行逻辑。Runnable的本质是把一段“无参数、无返回值”的可执行代码封装成一个任务,然后交给不同的执行机制去运行。它本身不代表线程,只代表“任务”。
Thread.start() 本质是启动一个新线程,等待操作系统调用,不阻塞当前线程。新线程被调度时,执行run() 方法。启动后处于就绪态,执行过程中可能存在以下情况:执行一段时间后让出资源进入阻塞状态,直到再次资源调度成功再进入就绪状态。run()方法执行完毕后,线程进入停止状态

不错? ? ?