本专栏主要对实习/校招遇到的技术面问题进行总结记录,下面将对某单位Java工程师(实习生)技术面的第一个问题进行详细记录与总结。
目录
4、Java多线程的实现方式③实现Callable接口通过FutureTask包装器来创建Thread线程
5、Java多线程的实现方式④使用ExecutorService、Callable、Future实现有返回结果的多线程
Q:同学你好,问你一下,Java多线程有几种实现方法?可以分别进行举例介绍吗?
A:一共有四种
1、继承Thread类;
2、实现Runnable接口;
3、实现Callable接口通过FutureTask包装器来创建Thread线程;
4、使用ExecutorService、Callable、Future实现有返回结果的多线程。
其中前两种方式线程执行完后都没有返回值,而后两种是带返回值的。
在JDK5.0之前,创建线程有2种方式,一种是继承Thread类,另外一种是实现Runnable接口。这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果,也就是无返回值。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。自Java 1.5起,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果(有返回值)。
详细举例介绍(这里是便于自己理解和总结列举了四个实例)
实例:用继承Thread类的方法进行多线程设计。两个线程为计算和显示质数。
- /**
- * @author 蓝多多的小仓库
- * @title: Java多线程的实现方式①继承Thread类
- * @projectName ex_thread
- * @description: 用继承Thread类的方法进行多线程设计。两个线程为计算和显示质数。
- * @date 2022/7/15 19:56
- */
- class primethread extends Thread {
- //自定义线程类primethread继承Thread类
- @Override
- //重写run()方法,编写线程执行体
- public void run() {
- int number=3;
- boolean flag=true;
- System.out.println(2);
- while(true)
- {
- loop:
- for(int i=2;i
- {
- if((number%i)==0)
- {
- flag=false;
- break loop;
- }
- }
- if(flag)
- {
- System.out.println(number);
- flag=false;
- }
- number++;
- flag=true;
- try
- {
- Thread.sleep(10000);
- }
- catch(InterruptedException e)
- {
- return;
- }
- }
- }
- }
- public class Athread
- {
- public static void main(String args[])
- {
- //新建一个线程
- primethread t_Thread = new primethread();
- //开启线程
- t_Thread.start();
- //此为主线程,main()方法中执行的主线程
- while(t_Thread.isAlive())
- {
- System.out.println("Counting the prime number..");
- try
- {
- Thread.sleep(10000);
- }
- catch(InterruptedException e)
- {
- return;
- }
- }
- }
- }
运行结果(这里展示两次运行的结果):

PS:
1>@Override的作用:
1)可以当注释用,方便阅读;
2)编译器可以验证@Override下面的方法名是否是父类中所有的,如果没有则报错。
例:如果没写@Override,而下面的方法名又写错了,这时编译器是可以编译通过的,因为编译器以为这个方法是你的子类中自己增加的方法。
2>start()方法调用后并不是立即执行多线程代码,而是使得该线程变为可运行态,什么时候运行是由操作系统决定的。从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。(根据两次运行结果不同可以看出)
3>Thread.sleep()方法调用目的:不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。
3、Java多线程的实现方式②实现Runnable接口
实例:用Runnable接口的方法进行多线程设计。线程的功能为输出正在执行的线程名。
- /**
- * @author 蓝多多的小仓库
- * @title: Java多线程的实现方式②实现Runnable接口
- * @projectName ex_thread
- * @description: 用Runnable接口的方法进行多线程设计。线程的功能为输出正在执行的线程名。
- * @date 2022/7/15 20:16
- */
- class show_Thread implements Runnable
- {
- private String t_name;
- public show_Thread(String t_name)
- {
- this.t_name=t_name;
- }
- @Override
- //重写Run()方法
- public void run() {
- for (int i = 0; i < 20; i++) {
- System.out.println(t_name + "正在运行: " + i);
- try
- {
- Thread.sleep(1000);
- }
- catch(InterruptedException e)
- {
- return;
- }
- }
- }
- }
- public class Bthread {
- public static void main(String[] args)
- {
- new Thread(new show_Thread("T_A")).start();
- new Thread(new show_Thread("T_B")).start();
- }
- }
运行结果:

4、Java多线程的实现方式③实现Callable接口通过FutureTask包装器来创建Thread线程
实例:遍历50以内的奇数,并对50以内奇数进行求和
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
- /**
- * @author 蓝多多的小仓库
- * @title: Java多线程的实现方式③实现Callable接口通过FutureTask包装器来创建Thread线程
- * @projectName ex_thread
- * @description: 遍历50以内的奇数,并对50以内奇数进行求和
- * @date 2022/7/16 20:06
- */
- class TolThread implements Callable
{ - // 重写call()方法
- @Override
- public Integer call() throws Exception
- {
- int total = 0;
- for (int i=1;i<=50;i++)
- {
- if (i%2 != 0)
- {
- System.out.println(i);
- total += i;
- }
- }
- return total;
- }
- }
- public class Cthread {
- public static void main(String[] args) {
- //创建Callable接口实现类的对象
- TolThread tolThread = new TolThread();
- //将该Callable实现类的对象作为参数传到FutureTask构造器中,创建FutureTask的对象
- FutureTask futureTask = new FutureTask(tolThread);
- //将FutureTask的对象作为参数传到Thread类的构造器中,创建Thread对象,并调用start方法
- new Thread(futureTask).start();
- try {
- //get方法的返回值为FutureTask构造器参数Callable实现类重写的call方法的返回值。
- System.out.println("总和为:"+futureTask.get());
- }
- catch (InterruptedException e)
- {
- e.printStackTrace();
- }
- catch (ExecutionException e)
- {
- e.printStackTrace();
- }
- }
- }
运行结果:

PS:
与使用Runnable相比, Callable功能更强大些:1>相比run()方法,可以有返回值2>方法可以抛出异常3>支持泛型的返回值4>需要借助FutureTask类,比如获取返回结果。
5、Java多线程的实现方式④使用ExecutorService、Callable、Future实现有返回结果的多线程
实例参考:https://blog.csdn.net/pdw2009/article/details/87939915
- /**
- * @author 蓝多多的小仓库
- * @title: Java多线程的实现方式④使用ExecutorService、Callable、Future实现有返回结果的多线程
- * @projectName ex_thread
- * @description: 代码来源:https://blog.csdn.net/pdw2009/article/details/87939915
- * @date 2022/7/16 20:50
- */
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Future;
-
- public class Dthread {
- public static void main(String[] args) {
- //创建一个线程池
- ExecutorService pools = Executors.newFixedThreadPool(5);
- List
> list = new ArrayList>(); - //创建多个有返回值的任务
- for(int i = 0 ; i <= 10 ; i++){
- Future
futures = pools.submit(new Task(i)); - list.add(futures);
- }
- for(Future
f : list){ - try {
- System.out.println(f.get());
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- }
- pools.shutdown();
- }
- }
- class Task implements Callable
- {
- private Integer taskID;
- public Task(Integer taskID) {
- this.taskID = taskID;
- }
- public Integer call() throws Exception {
- if(taskID.equals(3))
- Thread.sleep(1000);
- System.out.println("任务["+taskID+"]开始执行");
- return taskID;
- }
- }
运行结果:

PS:
可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。