1. 实现Java多线程
有三种使用线程的方法:
- 实现 Runnable 接口;
- 实现 Callable 接口;
- 继承 Thread 类。
实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。
1.1 继承Thread类,重写run()方法;
class MyThread extends Thread{
public void run(){
System.out.println("Thread body");
}
}
public class Test{
public static void main(String[] args){
MyThread thread=new MyThread();
thread.start();
}
}
1.2 实现Runnable接口,并实现该接口的run()方法;
推荐这个!!(实现这个接口的类还可以继承自其它的类,用3.1就没有办法再去继承别的类)
Step1:自定义类并实现Rubbale接口,实现run()方法;
Step2:创建Thread对象,用实现Runnable接口的对象作为参数实例化Thread对象;
Step3:调用Thread的start()方法。
class MyThread implements Runnable{
public void run(){
System.out.println("Thread body");
}
}
public class Test{
pulic static void main(String[] args){
MyThread thread=new MyThread();
Thread t=new Thread(thread);
t.start();
}
}
1.3 实现Callable接口,重写call()方法。
Callable接口实际上是属于Executor框架中的功能类.
Callable接口与Runnable接口的功能类似,提供了比Runnable更强大的功能,主要表现有三点:
- Callable可以在任务结束之后提供一个返回值,返回值通过 FutureTask 进行封装,Runnable则不能;
- Callable接口中的call()方法可以抛出异常,而Runnable()的run()方法不能抛出异常;
- 运行Callable可以拿到一个Future对象,Future对象表示异步计算的结果,提供了检查计算是否完成的方法。当调用future的get()方法以获取结果时,当前线程就会阻塞,直到call()方法结束返回结果。
public static class CallableTest implements Callable<String>{
@Override
public String call() throws Exception {
return "hello word";
}
}
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();//1.创建线程池
Future<String> future = threadPool.submit(new CallableTest());//2.提交任务
System.out.println("waiting thread to finish");
try {
System.out.println(future.get());//3.获取返回值
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
运行结果:
以上三种执行方式,前两种执行完后都没有返回值,最后一种带返回值。
当实现多线程时,一般推荐实现Runnable接口。
一个类是否可以同时继承Thread与实现Runnable?
可以。
public class Test extends Thread implements Runnable{
public static void main(String[] args){
Thread t=new Thread();
t.start();
}
}
其中,Test类从Thread类继承的run()被认为是对Runnable接口中run()的实现。
1.4 三种实现方式的比较
- 实现Runnable接口:
- 可以避免Java单继承特性而带来的局限;
- 增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;
- 适合多个相同程序代码的线程区处理同一资源的情况。
- 继承Thread类和实现Runnable方法启动线程都是使用start方法,然后JVM虚拟机将此线程放到就绪队列中,如果有处理机可用, 则执行run方法。
- 实现Callable接口要实现call方法,并且线程执行完毕后会有返回值。其他的两种都是重写run方法,没有返回值。