在Java中,多线程主要的实现方式有四种:继承Thread类、实现Runnable接口、实现Callable接口经过FutureTask包装器来建立Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,然后两种是带返回值的。除此以外,经过Timer启动定时任务,或者经过像Spring Task和quartz这样的第三方任务调度框架也能够开启多线程任务。java
一、继承Thread类建立线程git
Thread类本质上也是实现了Runnable接口的一个实例,表明一个线程的实例。启动线程的惟一方法就是经过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程比较简单,经过继承Thread类并复写run()方法,就能够启动新线程并执行本身定义的run()方法。多线程
CreateThreadDemo1.java并发
public class CreateThreadDemo1 extends Thread { public CreateThreadDemo1(String name) { // 设置当前线程的名字 this.setName(name); } @Override public void run() { System.out.println("当前运行的线程名为: " + Thread.currentThread().getName()); } public static void main(String[] args) throws Exception { // 注意这里,要调用start方法才能启动线程,不能调用run方法 new CreateThreadDemo1("MyThread1").start(); new CreateThreadDemo1("MyThread2").start(); } }
输出结果:框架
当前运行的线程名为: MyThread1
当前运行的线程名为: MyThread2
二、实现Runnable接口建立线程
因为Java是单继承机制,若是本身的类已经继承自另外一个类,则没法再直接继承Thread类,此时,能够经过实现Runnable接口来实现多线程。异步
实现Runnable接口并实现其中的run方法,而后经过构造Thread实例,传入Runnable实现类,而后调用Thread的start方法便可开启一个新线程。ide
CreateThreadDemo2.javathis
public class CreateThreadDemo2 implements Runnable { @Override public void run() { System.out.println("当前运行的线程名为: " + Thread.currentThread().getName()); } public static void main(String[] args) throws Exception { CreateThreadDemo2 runnable = new CreateThreadDemo2(); new Thread(runnable, "MyThread1").start(); new Thread(runnable, "MyThread2").start(); } }
输出结果:spa
当前运行的线程名为: MyThread1
当前运行的线程名为: MyThread2
三、实现Callable接口经过FutureTask包装器来建立Thread线程线程
首先须要一个实现Callable接口的实例,而后实现该接口的惟一方法call逻辑,接着把Callable实例包装成FutureTask传递给Thread实例启动新线程。FutureTask本质上也实现了Runnable接口,因此一样能够用来构造Thread实例。
CreateThreadDemo3.java
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class CreateThreadDemo3 { public static void main(String[] args) throws Exception { // 建立线程任务,lambada方式实现接口并实现call方法 Callable<Integer> callable = () -> { System.out.println("线程任务开始执行了..."); Thread.sleep(2000); return 1; }; // 将任务封装为FutureTask FutureTask<Integer> task = new FutureTask<>(callable); // 开启线程,执行线程任务 new Thread(task).start(); // ==================== // 这里是在线程启动以后,线程结果返回以前 System.out.println("线程启动以后,线程结果返回以前..."); // ==================== // 随心所欲完毕以后,拿到线程的执行结果 Integer result = task.get(); System.out.println("主线程中拿到异步任务执行的结果为:" + result); } }
输出结果:
线程启动以后,线程结果返回以前...
线程任务开始执行了...
主线程中拿到异步任务执行的结果为:1
四、使用ExecutorService、Callable、Future实现有返回结果的线程(线程池方式)
ExecutorService、Callable、Future三个接口都是属于Executor框架。可返回值的任务必须实现Callable接口。经过ExecutorService执行Callable任务后,能够获取到一个Future的对象,在该对象上调用get()就能够获取到Callable任务返回的结果了。
注意:Future的get方法是阻塞的,即:线程无返回结果,get方法会一直等待。
CreateThreadDemo4.java
import java.util.ArrayList; import java.util.Date; 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 CreateThreadDemo4 { @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("---- 主程序开始运行 ----"); Date startTime = new Date(); int taskSize = 5; // 建立一个线程池,Executors提供了建立各类类型线程池的方法,具体详情请自行查阅 ExecutorService executorService = Executors.newFixedThreadPool(taskSize); // 建立多个有返回值的任务 List<Future> futureList = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable callable = new MyCallable(i); // 执行任务并获取Future对象 Future future = executorService.submit(callable); futureList.add(future); } // 关闭线程池 executorService.shutdown(); // 获取全部并发任务的运行结果 for (Future future : futureList) { // 从Future对象上获取任务的返回值,并输出到控制台 System.out.println(">>> " + future.get().toString()); } Date endTime = new Date(); System.out.println("---- 主程序结束运行 ----,程序运行耗时【" + (endTime.getTime() - startTime.getTime()) + "毫秒】"); } } class MyCallable implements Callable<Object> { private int taskNum; MyCallable(int taskNum) { this.taskNum = taskNum; } public Object call() throws Exception { System.out.println(">>> " + taskNum + " 线程任务启动"); Date startTime = new Date(); Thread.sleep(1000); Date endTime = new Date(); long time = endTime.getTime() - startTime.getTime(); System.out.println(">>> " + taskNum + " 线程任务终止"); return taskNum + "线程任务返回运行结果, 当前任务耗时【" + time + "毫秒】"; } }
输出结果:
---- 主程序开始运行 ---- >>> 0 线程任务启动 >>> 1 线程任务启动 >>> 2 线程任务启动 >>> 3 线程任务启动 >>> 4 线程任务启动 >>> 0 线程任务终止 >>> 1 线程任务终止 >>> 0线程任务返回运行结果, 当前任务耗时【1001毫秒】 >>> 1线程任务返回运行结果, 当前任务耗时【1001毫秒】 >>> 4 线程任务终止 >>> 3 线程任务终止 >>> 2 线程任务终止 >>> 2线程任务返回运行结果, 当前任务耗时【1001毫秒】 >>> 3线程任务返回运行结果, 当前任务耗时【1001毫秒】 >>> 4线程任务返回运行结果, 当前任务耗时【1001毫秒】 ---- 主程序结束运行 ----,程序运行耗时【1009毫秒】
五、其余建立线程的方式
固然,除了以上四种主要的线程建立方式以外,也还有不少其余的方式能够启动多线程任务。好比经过Timer启动定时任务,或者经过像Spring Task和quartz这样的第三方任务调度框架也能够开启多线程任务,关于第三方任务调度框架的例子还请查询相关资料。
码云:https://gitee.com/liuge1988/java-demo.git
做者:朝雨忆轻尘
出处:https://www.cnblogs.com/xifengxiaoma/ 版权全部,欢迎转载,转载请注明原文做者及出处。