2.1 Thread类和Runnable接口
2.1.1 继承Tread类
首先是继承Tread类:
/**
* @author :ls
* @date :Created in 2022/4/18 15:10
* @description:
*/
public class T1 {
public static class MyThread extends Thread{
@Override
public void run() {
System.out.println("MyThread!");
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
注意要调用 start() 方法后,该线程再算启动。
2.1.2 实现Runnable接口
Runnable接口源码
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
我们可以使用java8函数式编程来简化一下代码:
/**
* @author :ls
* @date :Created in 2022/4/18 15:10
* @description:
*/
public class T1 {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
// Java 8 函数式编程,可以省略MyThread类
new Thread(() -> {
System.out.println("Java 8 匿名内部类");
}).start();
}
}
2.1.3 Thread类构造方法
Thread类是一个 Runnable 接口的实现类
查看Thread类的构造方法,发现其实是简单调用一个私有的init 方法来实现初始化的。
//Thread类源码
//片段1 init方法
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals)
//片段二 构造函数
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//片段三 在init方法中初始化的 AccessControlContext
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
//片段四 两个支持ThreadLocal的私有属性
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
解释一下 这些个参数:
- g : 线程组,指定这个线程是在哪个线程组下。
- target : 指定要执行的任务。
- name : 线程的名字,多个线程的名字是可以重复的。如不指定名字,见片段二。
- acc : 见片段三,用于初始化私有变量inheritedAccessControlContext。
- inheritThreadLocals : 可继承的 ThreadLocal ,见片段4,Thread类里面有量的私有属性来支持ThreadLocal。
一般来说我们用的最多的是:
Thread(Runnable target)
Thread(Runnable target, String name)
2.1.3 Thread类的几个常用方法
- currentThread() : 静态方法,返回对当前正在执行的线程对象的引用。
- start() : 开始执行线程的方法,java虚拟机会调用线程内部的run()方法。
- yield() : 放弃当前线程占用的cup资源。这里需要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程。
- sleep() : 静态方法,使用当前线程睡眠一段时间。
- join() : 使用当前线程等待另一个线程执行完毕之后在继续执行,内部调用是Object类的wait()方法实现的。
2.1.4 Thread类与Runnable接口的比较
- 由于java“单继承,多实现”的特性,Runnable接口使用起来比Thread更灵活。
- Runnable接口出现更符合面向对象,将线程单独进行对象的封装。
- Runnable接口出现,降低了线程对象和线程任务的耦合性。
- 如果使用线程时不需要使用Thread类的诸多方法,显然使用Runnable接口更为轻量。
2.2 Callable、Future与FutureTask
2.2.1 Callbale接口
Callable与Runnable类似,同样是只有一个抽象方法的函数式接口。不同的是,callable提供的方法是有返回值的,而且支持泛型。
@FunctionalInterface
public interface Callable {
V call() throws Exception;
}
callable一般是配合线程池工具 ExecutorService 来使用的。通过 submit 方法来让一个 Callable接口执行。它会返回一个 Future ,通过 Future 的 get 方法可以得到返回的结果。
/**
* @author :ls
* @date :Created in 2022/4/18 22:46
* @description:
*/
public class T2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// 模拟计算需要⼀秒
Thread.sleep(1000);
return 2;
}
public static void main(String args[]) throws ExecutionException, InterruptedException {
// 使⽤
ExecutorService executor = Executors.newCachedThreadPool();
T2 task = new T2();
Future<Integer> result = executor.submit(task);
// 注意调⽤get⽅法会阻塞当前线程,直到得到结果。
// 所以实际编码中建议使⽤可以设置超时时间的重载get⽅法。
System.out.println(result.get());
}
}
输出结果:
2
2.2.2 Future 接口
public abstract interface Future<V> {
public abstract boolean cancel(boolean mayInterruptIfRunning);
public abstract boolean isCancelled();
public abstract boolean isDone();
public abstract V get() throws InterruptedException, ExecutionException;
public abstract V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
cancel 方法是试图取消一个线程的执行。 注意这里是试图取消,并不一定成功。因为任务可能已完成、或者一些其他因素不能取消,存在取消失败可能。
返回值 表示“是否取消成功”
参数 mayInterruptIfRunning 表示是否采用中断的方式取消线程。
因此为了让任务有能够取消的功能,就使用callable来代替runnable。如果为了可取消性⽽使⽤ Future 但⼜不提供可⽤的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。
2.2.3 FutureTask类
这个接口有一个实现类叫 FutureTask 。 FutureTask 是 实现的 RunnableFuture 接口的,而 RunnableFuture 接口同时继承了 Runnable 接口 和 Future 接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
Future 只是一个接口,而它里面的 cancel , get , isDone 等方法要自己实现 起来都是非常复杂的。所以JDK提供了一个 FutureTask 类来供我们使用。
/**
* @author :ls
* @date :Created in 2022/4/18 23:15
* @description:
*/
public class T3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 模拟计算需要⼀秒
Thread.sleep(1000);
return 2;
}
public static void main(String args[]) throws ExecutionException, InterruptedException {
// 使⽤
ExecutorService executor = Executors.newCachedThreadPool();
FutureTask<Integer> futureTask = new FutureTask<>(new T3());
Future<?> submit = executor.submit(futureTask);
System.out.println(futureTask.get());
}
}
使用上与第一个Demo有一点小的区别。首先,调用 submit 方法是没有返回值的。 这里实际上是调用的 submit(Runnable task) 方法,而上面的Demo,调用的 是 submit(Callable task) 方法。
然后,这里是使用 FutureTask 直接取 get 取值,而上面的Demo是通过 submit 方 法返回的 Future 去取值。
在很多高并发的环境下,有可能Callable和FutureTask会创建多次。FutureTask能 够在高并发环境下确保任务只执行一次。
2.2.3 FutureTask的几个状态
/**
* state可能的状态转变路径如下:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state; //任务的运⾏状态
private static final int NEW = 0; //初始状态为NEW
private static final int COMPLETING = 1; //完成状态/结束
private static final int NORMAL = 2; //正常的
private static final int EXCEPTIONAL = 3; //异常的
private static final int CANCELLED = 4; //已取消
private static final int INTERRUPTING = 5; //打断
private static final int INTERRUPTED = 6; //已中断