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()方法,有返回值
线程运行状态
使用start()方法后,线程并未立即执行,而是进入就绪状态,等待资源调度
资源调度成功后进入运行状态,执行run()方法,执行一段时间后让出资源进入阻塞状态,直到再次资源调度成功再进入就绪状态
run()方法执行完毕后,线程进入停止状态
不错? ? ?