1. Java 中多线程的实现
主要有三种方案:
- 继承 Thread 类,并重写 run() 方法
- 实现 Runnable 接口,并重写 run() 方法
- 实现 Callable 接口,并重写 call() 方法
其中前两种比较常见。
1.1 Thread 类
继承 Thread
类,并重写 run()
方法。
Thread 类底层也是实现了 Runnable 接口,并重写了 run() 方法。
调用 start()
方法后,JVM为创建一个新的线程,并将该线程设置为可运行态 Runnable,但并没有直接运行。
- 当线程第一次得到时间片时,
run()
方法得以运行。
public class Demo {
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}
start() 方法不可被多次调用,否则会抛出异常。
1.1.1 Thread 类常用方法
Thread.currentThread()
:静态方法,返回对当前正在执行线程对象的引用start()
: 开始线程,把新线程设置为就绪态。run()
: 线程获得时间片时,自动被异步调用,真正开始执行该线程。- 该方法实现自
Runnable
接口。
- 该方法实现自
yield()
:让出当前的 CPU 时间片,并重新变成就绪状态,重新竞争 CPU。- 让出后,可能当前 CPU 使用权还会被该线程获取到。
Thread.sleep()
: 静态方法,让当前线程睡眠一段时间。join()
: 让当前线程等待另一个线程执行完毕后再继续执行。- 底层是使用的
Object
类的wait()
方法
- 底层是使用的
yield() 和 sleep() 的异同
- 都能暂停当前线程,yield() 依赖于 CPU 时间片划分,sleep() 可以指定具体休眠时间
- 二者若持有锁,则在暂停线程时都不会释放锁
- yield() 不能被中断,sleep() 可以被中断
1.2 Runnable 接口
Runnable
接口是一个函数式接口,支持函数式编程。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
通过 Runnable
接口来创建并启动线程,有两种方式:
- 通过 普通类 的方式或 函数式编程匿名类 的方式来创建
Runnable
接口的实现类,并实现run()
方法。 - 传入
Runnable
的实现类,实例化Thread
类对象。 - 调用
start()
方法来启动该线程。
public class Demo {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
// 通过 Thread 类来创建新线程
new Thread(new MyThread()).start();
// Java 8 函数式编程,创建匿名类
new Thread(() -> {
System.out.println("Java 8 匿名内部类");
}).start();
}
}
1.3 联系与区别
继承 Thread 类或者实现 Runnable 接口,最终都是通过创建 Thread 对象来控制线程的创建。
推荐使用 Runnable 的方式来实现多线程。