多线程的概述及创建方式
1.什么是线程?
线程是程序内部的一条执行流程,程序中如果只有一条执行流程,这个程序就是单线程的程序
2.多线程的概述
多线程是指从软硬件上实现的多条执行流程的技术(多条线程由CPU负责调度执行),例如:消息通信、淘宝、京东系统都离不开多线程技术
3.创建多线程三种方式
Java是通过java.lang.Thread类的对象来代表线程的
-
创建方式一:继承Thread类
①任务类继承Thread类,重写run方法
②测试类中,创建任务类对象
③调用继承来的start方法启动线程
方式一优缺点:
- 优点:编码简单,由于继承Thread类,可以直接调用Thread类的方法
- 缺点:已经继承Thread类,不能再继承其他类,扩展性低
任务类:
//任务类继承Thread类,重写run方法
public class MyThread extends Thread{
@Override
public void run() {
//子线程任务:打印整数1到10
for (int i = 1; i <= 100 ; i++) {
System.out.println("子线程" + i);
}
}
}
测试类:
public class Demo {
public static void main(String[] args) {
//测试类中,创建任务类对象
MyThread t = new MyThread();
//调用继承来的start方法启动线程
t.start();
//主线程任务:打印整数1到10
for (int i = 1; i <= 100 ; i++) {
System.out.println("主线程" + i);
}
}
}
-
创建方式二:实现Runnable接口
①任务类实现Runnable接口,重写run方法
②测试类中,创建任务类对象,作为参数传递给Thread类的构造
public Thread(Runnable target):封装Runnable对象成为线程对象
③Thread对象调用start方法启动线程
方式二的优缺点
- 优点:任务类只是实现接口,可以继续继承其他类,实现其他接口,扩展性强
- 缺点:编码复杂,需要多一个Runnable对象,而没有直接继承Thread类,所以不能直接调用Thread类的方法
任务类:
//任务类类实现Runnable接口,重写run方法 public class MyRunnable implements Runnable { @Override public void run() { //子线程任务 for (int i = 1; i <= 100; i++) { System.out.println("大炮:" + i); } } }
测试类:
public class Demo {
public static void main(String[] args) {
//测试类中,创建任务类对象,作为参数传递给Thread类的构造
MyRunnable target = new MyRunnable();
//Thread对象调用start方法启动线程
Thread t = new Thread(target);
t.start();
//主线程任务
for (int i = 1; i <= 100; i++) {
System.out.println("飞机:" + i);
}
}
}
-
创建方式三:实现Callable接口
线程执行完毕后有一些数据需要返回,前两种方式重写的run方法均不能直接返回结果
解决问题:
① JDK5.0提供了Callable接口和FutureTask类来实现
②这种方式的最大优点:可以返回线程执行完毕后的结果
步骤:
任务类实现Callable接口,重写call方法,和要返回的数据类型
Callable为泛型接口,泛型为返回结果的数据类型
任务类对象不能直接交给Thread类的构造,需要封装为FutureTask对象
测试类中,创建任务对象,作为参数传递给FutureTask类的构造,得到FutureTask对象
将FutureTask对象作为参数传递给Thread类的构造
Thread对象调用start方法启动线程
线程执行完毕后,调用FutureTask对象的get方法,获取返回的数据
FutureTask类的方法:
public FutureTask(Callable call);:将Callable对象封装为FutureTask任务类对象
public V get() throws Exception:获取call方法的返回值,get方法是阻塞的
方式三的优缺点
- 优点:任务类只是实现接口,可以继续继承其他类,实现其他接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果
- 缺点:编码复杂
任务类:
//任务类实现Callable接口,重写call方法,和要返回的数据
class MyCallable implements Callable<Integer> {
//通过构造注入一个整数n
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
}
测试类:
public class Demo03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//测试类中,创建任务对象,作为参数传递给FutureTask类的构造,得到FutureTask对象
FutureTask<Integer> ft = new FutureTask<>(new MyCallable(100)); //求1到100的整数和
//将FutureTask对象,作为参数传递给Thread类的构造
Thread t = new Thread(ft);
//Thread对象调用start方法启动线程
t.start();
//线程执行完毕后,调用FutureTask对象的get方法,获取返回的数据
System.out.println(ft.get()); //5050
}
}
4.多线程的注意事项
(1)启动线程必须是调用start方法,不是调用run方法,直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
(2)不能把主线程任务放在自动子线程之前,这样主线程一直是先跑完,相当于一个单线程的效果