Thread类和Runnable接口
JDK中提供Tread类和Runnable接口供我们创建线程类实现多线程功能。
继承Tread类,重写Run方法;实现Runnable接口的run方法。
Thread类
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方法,而不是run方法。run方法只是线程执行的功能,而start方法才是启动一个线程。
当我们调用start方法后,虚拟机会先为我们创建线程,然后等到该线程分配到时间片后再调用run方法。第一次调用start方法后再次调用会抛出异常。
Runnable接口
runnable接口内部就run这一个方法
public interface Runnable {
public abstract void run();
}
实现
public class Demo {
public static class Mythread implements Runnable{
@Override
public void run() {
System.out.println("继承接口实现");
}
}
public static void main(String[] args) {
Mythread my = new Mythread();
Thread thread1 = new Thread(my);
thread1.start();
}
}
Threa的构造方法
从上面可以发现我实现runnable接口时将该类对象放入到了Tthread类中,调用了Thread类的构造方法。下面我将说一点Thread的源码。
//Threa类继承了 Runnable接口
class Thread implements Runnable
几个构造方法
//上面用到的构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
这里的构造器都调用了init方法来进行初始化。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals)
参数:
- g:线程组
- target:要执行的任务
- name:线程的名字
- acc:用于初始化私有变量
inheritedAccessControlContext。 - inheritThreadLocals:可继承的
ThreadLocal
Thread类的方法
- currentThread():静态方法,返回对当前正在执行的线程对象的引用;
- start():开始执行线程的方法,java虚拟机会调用线程内的run()方法;
- yield():yield在英语里有放弃的意思,同样,这里的yield()指的是当前线程愿意让出对当前处理器的占用。这里需要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的;
- sleep():静态方法,使当前线程睡眠一段时间;
- join():使当前线程等待另一个线程执行完毕之后再继续执行,内部调用的是Object类的wait方法实现的;
Runnable接口和TThread类的比较
实现一个线程可以使用Runnable和Thread,他们的具体差别如下:
- 由于java中单继承多实现的特性,使用Runnable接口更灵活
- 使用接口实现更符合java面向对象的特征,这样会使线程的功能和线程都创建对象。
- 这样做也会降低线程对象和线程任务间的耦合。
- 如果使用线程不需要使用Thread的多种方法,那么使用Runnable接口更为轻量。
Callable、Future与FutureTask
上面提到的创建使用线程的方法有一个问题,那就是这么做会没有返回值,为了解决这个问题,jdk为我们提供了Callable接口与Future类。
Callable接口
public interface Callable<V> {
V call() throws Exception;
}
不仅有返回值而且还支持泛型。
Future接口
- Future接口用来表示异步执行的结果,比如异步执行一个Callable任务就会返回一个Future对象,通过这个Future对象来获取异步执行的结果
public interface Future<V> {
/**
* 1.尝试取消任务,如果任务已经完成或者被取消则会取消失败,也有可能因为其他原因取消失败,方法返回true表示取消成功,
* false表示取消失败,通常是因为任务已经开始了
* 2.如果任务还未开始并且被取消成功,那么这个任务再也不会被执行
* 3.如果任务已经开始,mayInterruptIfRunning参数决定执行该任务的线程是否需要中断任务,true表示正在执行任务的线程需要中断,false表示既然已经开始执行了,就执行完毕吧
* 4.如果方法返回true,那么isDone和isCancelled也会返回true
*/
boolean cancel(boolean mayInterruptIfRunning);
//如果任务在完成之前被取消,返回true
boolean isCancelled();
//如果任务已经完成,返回true,这里的完成不仅仅是代表正常完成,也有可能是异常终止,取消,这些都会返回true
boolean isDone();
//阻塞获取异步执行的结果
V get() throws InterruptedException, ExecutionException;
//指定超时时间的get超时
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
cancel是试图取消,并不一定能取消成功。因为任务可能已完成、已取消、或者一些其它因素不能取消,存在取消失败的可能。boolean类型的返回值是“是否取消成功”的意思。参数paramBoolean表示是否采用中断的方式取消线程执行。
FutureTask类
FutureTask类实现了RunnableFuture类,而RunnableFuture类又继承自Runnable和Future
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
因为Future中一些方法的具体实现很复杂,所以jdk为我们提供了一个实现类。
具体实现
通过callable实现多线程,我们可以配合线程池工具ExecutorService来使用。ExecutorService可以使用submit方法来让一个Callable接口执行。它会返回一个Future,我们后续的程序可以通过这个Future的get方法得到结果。
// 自定义Callable
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// 模拟计算需要一秒
Thread.sleep(1000);
return 2;
}
public static void main(String args[]){
// 使用
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
// 注意调用get方法会阻塞当前线程,直到得到结果。
// 所以实际编码中建议使用可以设置超时时间的重载get方法。
System.out.println(result.get());
}
}
通过FutureTask实现多线程
// 自定义Callable,与上面一样
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// 模拟计算需要一秒
Thread.sleep(1000);
return 2;
}
public static void main(String args[]){
// 使用
ExecutorService executor = Executors.newCachedThreadPool();
FutureTask<Integer> futureTask = new FutureTask<>(new Task());
executor.submit(futureTask);
System.out.println(futureTask.get());
}
}