前言
大家好,牧码心今天给大家推荐一篇并发编程系列之基础篇(二)—常用多线程实现的方式的文章,希望对你有所帮助。具体内容如下
- Thread,Runnable和Callable声明
- 实例演示
- 主要区别
概要
我们知道,创建线程通常的两种方式:一种是直接继承Thread,另外一种是实现Runnable接口。这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
从Java 1.5开始,提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。Future接口是Java多线程Future模式的实现。Future模式是多线程设计常用的一种设计模式。
Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间后,我便可以从Future那儿取出结果。具体关于Future后续会详解。
本文将从这几种方式定义,具体实例和主要区别等方面介绍。
Thread,Runnable和Callable声明
- Thread 是一个类,可以创建线程。Thread本身就实现了Runnable接口,具体定义如下:
class Thread implements Runnable {...}
- Runnable 是一个接口,该接口中只包含了一个run()方法,由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。如定义一个类A实现Runnable接口;然后通过new Thread(new A())的方式新建线程。具体定义如下:
public interface Runnable {
public abstract void run();
}
- Callable定义的是一个泛型接口,在它里面声明了一个call()函数,返回的类型就是传递进来的V类型,具体定义如下:
public interface Callable<V> {
V call() throws Exception;
}
实例演示
- 继承Thread类
/**
* 继承Thead类
* 目标:创建2个线程并发实现从1-100的累加
*/
public class CountThread01 extends Thread {
private int start,end;
private int num=0;
public CountThread01(String name, int start, int end){
super(name);
this.start=start;
this.end=end;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"执行结束,num="+num);
}
public static void main(String[] args) throws InterruptedException {
int start=0,mid=50,end=100;
CountThread01 t1=new CountThread01("线程1",start,mid);
CountThread01 t2=new CountThread01("线程2",mid+1,end);
t1.start();
t2.start();
//合并2个线程
t1.join();
t2.join();
System.out.println("结果:"+(int)(t1.num+t2.num));
}
}
实现步骤如下:
- 继承 Thread 类
- 覆盖 run() 方法
- 直接调用 Thread#start() 执行
- 实现Runnable接口方式
/**
* 实现Runnable
*/
public class CountThread02 implements Runnable {
private int start,end;
private int num=0;
public CountThread02(int start,int end){
this.start=start;
this.end=end;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"执行结束,sum="+num);
}
public static void main(String[] args) throws InterruptedException {
int start=0,mid=50,end=100;
CountThread02 count1=new CountThread02(start,mid);
CountThread02 count2=new CountThread02(mid+1,end);
Thread t1=new Thread(count1,"线程1");
Thread t2=new Thread(count2,"线程2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("结果:"+(int)(count1.num+count2.num));
}
}
实现步骤如下:
- 实现Runnable接口;
- 执行Runnable实例,作为入参,创建Thread实例;
- 执行Thread#start方法
- 实现Callable接口,结合FutureTask使用
/**
* 实现Callable接口,结合FutureTask使用
*/
public class CountThread03 implements Callable<Integer> {
private int start,end;
private int num=0;
public CountThread03(int start,int end){
this.start=start;
this.end=end;
}
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"执行");
for (int i =start; i <=end; i++) {
num+=i;
}
System.out.println(Thread.currentThread().getName()+"执行结束,num="+num);
return num;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
int start=0,mid=50,end=100;
int sum1=0,sum2=0;
FutureTask<Integer> task1=new FutureTask<Integer>(new CountThread03(start,mid));
FutureTask<Integer> task2=new FutureTask<Integer>(new CountThread03(mid+1,end));
Thread t1=new Thread(task1,"线程1");
Thread t2=new Thread(task2,"线程2");
t1.start();
t2.start();
t1.join();
t2.join();
if(task1.isDone()){
sum1=task1.get();
}
if(task1.isDone()){
sum2=task2.get();
}
System.out.println("结果:"+(int)(sum1+sum2));
}
}
实现步骤如下:
- 实现Callable接口;
- 以Callable的实现类为参数,创建FutureTask实例;
- 将FutureTask作为Thread的参数,创建Thread实例;
- 通过 Thread#start 启动线程;
- 通过 FutreTask#get() 阻塞获取线程的返回值
主要区别
这几种方式都能创建线程,其不同点如下:
- Thread 是一个类,而Runnable,Callable是一个接口;
- 继承是单继承,接口能多实现,也能继承;
- Callable可以直接获取返回结果,Thread和Runnable需要通过共享变量或者使用线程通信的方式获得返回结果;
- Runnable可以用于“资源的共享”,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。