多线程
什么是线程?
- 线程(Thread)是一个程序内部的一条执行路径。
- 我们之前启动程序执行后,main方法执行其实就是一条单独的执行路径
- 程序中如果只有一条执行路径,那么这个程序就是单线程的程序。
多线程是什么?
- 多线程是指从软硬件上实现多条执行流程的技术。
多线程的创建
Thread类
- Java是通过java.lang.Thread类来代表线程的。
- 按照面向对象的思想,Thread类应该提供了实现多线程的方式。
方式一:继承Thread类
- 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
- 创建MyThread类的对象
- 调用线程对象的start()方法启动线程(启动后还是执行run方法的)
public class ThreadDemo01 {
public static void main(String[] args) {
//3.new一个线程对象
Thread t = new MyThread();
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程启动了:"+i);
}
}
}
/**
1.定义一个线程类继承Thread类
*/
class MyThread extends Thread{
/**
2.重写run方法,里面定义线程以后要干啥
*/
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程启动了:"+i);
}
}
}
方式一优缺点:
- 优点:编码简单
- 缺点:线程类以及继承Thread,无法继承其他类,不利于拓展
- 为什么不直接调用了run方法,而是调用start启动线程
- 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
- 只有调用start方法才是启动一个新的线程执行
- 把主线程任务放在子线程之前了。
- 这样主线程一直是先跑完的,相当于是一个单线程的效果了。
方式二:实现Runnable接口
- 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable任务对象
- 把MyRunnable任务对象交给Thread处理
- 调用线程对象的start()方法启动线程
public class ThreadDemo02 {
public static void main(String[] args) {
Runnable runnable = new MyRunable();
Thread t = new Thread(runnable);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程开始执行:"+i);
}
}
}
class MyRunable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程在执行:"+i);
}
}
}
方式二优缺点:
- 优点:线程任务类只是实现接口,可以继续继承类和实现接口,拓展性强。
- 缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
多线程的实现方案二:实现Runnable接口(匿名内部类形式)
- 可以创建Runnable的匿名内部类对象
- 交给Thread处理
- 调用线程对象的start() 启动线程
public class ThreadDemoOther02 {
public static void main(String[] args) {
//创建一个任务对象
Runnable a = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程1执行输出:" + i);
}
}
};
// 把任务对象交给Thread处理
Thread t = new Thread(a);
t.start();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程2执行输出:" + i);
}
}
});
t1.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程3执行输出:" + i);
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("子线程4执行输出:" + i);
}
}).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行输出:" + i);
}
}
}
output:
子线程2执行输出:0
子线程2执行输出:1
子线程2执行输出:2
子线程2执行输出:3
子线程2执行输出:4
子线程1执行输出:0
子线程1执行输出:1
子线程1执行输出:2
子线程1执行输出:3
子线程1执行输出:4
主线程执行输出:0
主线程执行输出:1
主线程执行输出:2
主线程执行输出:3
主线程执行输出:4
子线程4执行输出:0
子线程4执行输出:1
子线程4执行输出:2
子线程4执行输出:3
子线程4执行输出:4
子线程3执行输出:0
子线程3执行输出:1
子线程3执行输出:2
子线程3执行输出:3
子线程3执行输出:4
方式三:JDK5.0新增:实现Callable接口
多线程的实现方案三:利用Callable、FutureTask接口实现
- 得到任务对象
- 定义类实现Callable接口,重写call方法,封装要做的事情
- 创建Callable任务对象交给FutureTask对象
- 用FutureTask 把Callable对象封装成线程任务对象
- 把线程任务对象交给Thread处理
- 调用Thread的start方法启动线程,执行任务
- 线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果
public class ThreadDemo03 {
public static void main(String[] args) {
Callable<String> call = new myCallable(100);
//FutureTask对象的作用:是Runnable的对象(实现了Runnable接口),可以交给Thread了
FutureTask<String> f = new FutureTask<>(call);
Thread t = new Thread(f);
t.start();
Callable<String> call2 = new myCallable(200);
FutureTask<String> f2 = new FutureTask<>(call2);
Thread t2 = new Thread(f2);
t2.start();
try {
String rs1 = f.get();
System.out.println(rs1);
} catch (Exception e) {
e.printStackTrace();
}
try {
String rs2 = f2.get();
System.out.println(rs2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class myCallable implements Callable<String> {
private int n;
public myCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i <= n; i++) {
sum += i;
}
return "子线程结果是:" + sum;
}
}
output:
子线程结果是:5050
子线程结果是:20100
FutureTask的API
方法名称 | 说明 |
---|---|
public FutureTask<>(Callable call) | 把Callable对象封装成FutureTask对象。 |
public V get() throws Exception | 获取线程执行call方法返回的结果 |
Thread的常用方法
Thread常用API说明
- Thread常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread()。
- 至于Thread类提供的诸如:yield、join、interrupt、不推荐的方法stop、守护线程、线程优先级等线程的控制方法,在开发中很少使用。
Thread类获得当前线程的对象
注意:
-
- 此方法是Thread类的静态方法,可以直接使用Thread类调用。
-
- 这个方法是在哪个线程执行中调用的,就会得到哪个线程对象。
public class ThreadDemo01