三种添加线程的方式及其区别

我们常见的添加线程的方法通常是两种:
①继承Thread类,实现run方法,调用start()方法开启线程;
②实现Runnable接口,实现run方法, 调用start()方法开启线程;
其实还有第三种常用的添加线程的方式:
通过Callable和Future创建线程


1. 继承Thread类添加线程

使用该方法添加线程的步骤是:
第一步:创建类继承Thread类
第二步:实现run方法,将任务写在run方法里面
第三步:在main方法new出该类,调用start方法开启线程

代码实现如下:


public class ThreadTest extends Thread {
	
	//实现run方法
	@Override
	public void run() {
		for(int i=0;i<=6;i++) {
		   System.out.println(Thread.currentThread().getName() + "--" + i);
		   try {
			Thread.sleep(new Random().nextInt(100));  //休眠
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
		System.out.println(Thread.currentThread().getName()+"--finsh");
	}
	
	
	public static void main(String[] args) {
		new ThreadTest("A").start();
		new ThreadTest("B").start();
	}
	
}

输出结果为(结果不唯一):

Thread0–1
Thread1–1
Thread0–2
Thread0–3
Thread1–2
Thread1–3
Thread0–4
Thread0–5
Thread0–6
Thread0–finish
Thread1–4
Thread1–5
Thread1–6
Thread1–finish

我们可以发现,继承Thread类创建的线程可以拥有自己独立的类成员变量i。

2.实现Runnable接口创建线程

使用该方法创建线程的步骤是:
第一步:创建类实现Runnable接口;
第二步:实现run方法,将任务写在run方法内;
第三步:创建Thread对象,创建Runnable实例,将其传入Thread对象中;
第四步:调用start方法开启线程。

代码实现如下:

public class RunableTest implements Runnable{
	private int j;
	
	//实现run方法
	@Override
	public void run() {
		for(int i=0;i<=10;i++) {
			   try {
				Thread.sleep(new Random().nextInt(100)); //休眠
				j++;
				 System.out.println(Thread.currentThread().getName()+"--"+j);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			}
			System.out.println(name+" finsh");
	}
 	
  public static void main(String[] args) {
	  RunableTest R=new RunableTest();
	  new Thread(R).start();
	  new Thread(R).start();
	}
}

执行结果为:

Thread0–1
Thread1–2
Thread0–3
Thread0–4
Thread1–5
Thread1–6
Thread0–7
Thread0–8
Thread0–9
Thread0–10

在加入休眠操作之后,可以发现,虽然线程不同,但是i却是进程共享的。

3.通过Callable和Future创建线程

先简单了解以下Callable和Future接口以及他的实现类FutureTask:

  • Callable是一个接口,它与Runnable极其相似,但在Runnable接口中run方法是线程执行体,而在Callable中call方法是线程执行体。
  • call能够实现run能实现的所有功能,除此之外还多出以下两个功能:
    • call方法允许有返回值,可以在执行完后返回数据;
    • call方法能够声明抛出的异常;
  • FutureTask类实现了Runnable和Future接口;
  • Future接口是对Callable任务的执行结果进行取消,查询是否完成。

使用该方法创建线程的步骤是:
第一步:创建类实现Callable接口;
第二步:实现call方法, 将任务放在call方法内;
第三步:创建Callable实例,创建FutureTask实例,将Callable实例传入FutureTask中;
第四步:创建Thread对象,将FutureTask实例传入Thread对象中;
第五步:调用start方法, 开启线程。

代码实现如下:


import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
 
public class CallableTest implements Callable<Integer> {

   //实现call方法
	@Override
	public Integer call() throws Exception {
		int i=0;
		for(;i<10;i++) {
			   System.out.println(Thread.currentThread().getName()+ "--" + i);
			   try {
				Thread.sleep(new Random().nextInt(100));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			}
			System.out.println("finsh");
		return i;
	}
 
public static void main(String[] args) {
	CallableTest callable1 = new CallableTest();
	CallableTest callable2 = new CallableTest();
	FutureTask<Integer> furureTask1 = new FutureTask<Integer>(callable1);
	FutureTask<Integer> futureTask2 = new FutureTask<Integer>(callable2);
	new Thread(Task1).start();
	new Thread(Task2).start();
	}
}

输出结果如下:

Thread0–1
Thread1–1
Thread0–2
Thread0–3
Thread1–2
Thread0–4
Thread0–5
Thread0–6
finish
Thread1–3
Thread1–4
Thread1–5
Thread1–6
finish

在该输出方法中,线程在执行过程中并不会输出返回值,如果要获得返回值则调用futureTask1.get()即可得到返回值。

4.区别:

三者区别在以上已经有所说明,总结来说有以下三点:

  1. 继承Thead类创建的线程可以拥有独立的成员变量,而实现Runnable接口创建的线程中的成员变量则是进程共享的;
  2. 三者的创建方式有所不同;
  3. 使用Callable和Task创建的线程的执行体与前两者的执行体不同,前两者是run方法作为执行体,后者是call方法作为执行体。call方法作为执行体有以下两个优势:1)call方法允许有返回值,可以在执行完后返回数据;
    2) call方法能够声明抛出的异常。

猜你喜欢

转载自blog.csdn.net/Zetang_Wu/article/details/83038854
今日推荐