Java 实现多线程的四种方式
在 Java 中实现多线程一共有四种方式:
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 Callable 接口
- 线程池
下面我将对这四种方式进行入门级的解析和演示。
一、继承 Thread 类
通过继承 Thread 类实现多线程的步骤如下:
- 创建 MyThread 类,让其继承 Thread 类并重写 run() 方法。
- 创建 MyThread 类的实例对象,即创建一个新线程。
- 调用 start() 方法,启动线程。
代码示例如下:
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("我是通过继承 Thread 类创建的多线程,我叫" + Thread.currentThread().getName());}
}class TestMyThread {public static void main(String[] args) {MyThread myThread1 = new MyThread();myThread1.setName("Thread-1");MyThread myThread2 = new MyThread();myThread2.setName("Thread-2");MyThread myThread3 = new MyThread();myThread3.setName("Thread-3");myThread1.start();myThread2.start();myThread3.start();}
}
为了演示线程执行顺序的随机性,我特意创建了三个线程,并为每一个线程命名,下面是我运行五次程序的执行结果:
// 第一次
我是通过继承 Thread 类创建的多线程,我叫Thread-2
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3// 第二次
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3
我是通过继承 Thread 类创建的多线程,我叫Thread-2// 第三次
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3
我是通过继承 Thread 类创建的多线程,我叫Thread-2// 第四次
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-2
我是通过继承 Thread 类创建的多线程,我叫Thread-3// 第五次
我是通过继承 Thread 类创建的多线程,我叫Thread-2
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3
从上面的执行结果我们可以看到线程的执行顺序和代码中编写的顺序没有关系,线程的执行顺序是具有随机性的。
二、实现 Runnable 接口
Runnable 接口只有一个 run() 方法,源码如下:
public interface Runnable {public abstract void run();
}
通过实现 Runnable 接口实现多线程的步骤如下:
- 创建 MyRunnable 类实现 Runnable 接口。
- 创建 MyRunnable 类的实例对象 myRunnable 。
- 把实例对象 myRunnable 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
- 调用 start() 方法,启动线程。
代码示例如下:
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("我是通过实现 Runnable 接口创建的多线程,我叫" + Thread.currentThread().getName());}
}class TestMyRunnable {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}
执行结果如下:
我是通过实现 Runnable 接口创建的多线程,我叫Thread-0
相比于继承 Thread 类的方法来说,实现 Runnable 接口是一个更好地选择,因为 Java 不支持多继承,但是可以实现多个接口。
有一点值得注意的是 Thread 类也实现了 Runnable 接口,这意味着构造函数 Thread(Runnable target) 不仅可以传入 Runnable 接口的对象,而且可以传入一个 Thread 类的对象,这样就可以将一个 Thread 对象中的 run() 方法交由其他线程进行调用。
三、实现 Callable 接口
Callable 接口只有一个 call() 方法,源码如下:
public interface Callable<V> {V call() throws Exception;
}
从源码我们可以看到 Callable 接口和 Runnable 接口类似,它们之间的区别在于 run() 方法没有返回值,而 call() 方法是有返回值的。
通过实现 Callable 接口实现多线程的步骤如下:
- 创建 MyCallable 类实现 Callable 接口。
- 创建 MyCallable 类的实例对象 myCallable。
- 把实例对象 myCallable 作为参数来创建 FutureTask 类的实例对象 futureTask。
- 把实例对象 futureTask 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
- 调用 start() 方法,启动线程。
代码示例如下:
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int a = 6;int b = 9;System.out.println("我是通过实现 Callable 接口创建的多线程,我叫" + Thread.currentThread().getName());return a + b;}
}class TestMyCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask(myCallable);Thread thread = new Thread(futureTask);thread.start();System.out.println("返回值为:" + futureTask.get());}
}
执行结果如下:
我是通过实现 Callable 接口创建的多线程,我叫Thread-0
返回值为:15
FutureTask 类提供了一个 get() 方法用来获取 call() 方法的返回值,但需要注意的是调用这个方法会导致程序阻塞,必须要等到线程结束后才会得到返回值。
四、线程池
在 Java 中构建一个新的线程是需要一定的系统开销的,前面三种实现多线程的方法在线程执行完任务后就会将线程销毁,那么是否可以在线程执行完任务后将线程保存下来,给下一个任务使用呢?答案是可以的,为了解决这个问题,线程池应运而生。
顾名思义,线程池就是用来存储线程的池子。线程池中包含许多准备运行的线程,我们只需要为线程池提供一个个任务,线程池就会按照一定的规则去调用这些任务,当一个任务完成后,调用这个任务的线程不会死亡,而是留在线程池中准备为下一个任务提供服务。
Executors 类提供了许多静态工厂方法用来构造线程池,这里我介绍其中的三种:
newFixedThreadPool(int nThreads)
该方法用来构造一个固定大小的线程池,空闲的线程会一直保留着,如果提交的任务数多于空闲线程数,就会把未得到服务的任务放到队列中等待。
代码示例如下:
public class MyFixedThreadPool {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(3);// 调用 Runnable 任务TestRunnable1 testRunnable1 = new TestRunnable1();for (int i = 0; i < 5; i++) {// 调用 Runnable 任务可以用以下两种方法,二者的区别在于前者没返回值,后者有返回值executorService.execute(testRunnable1);Future<?> submit = executorService.submit(testRunnable1);}// 调用 Callable 任务TestCallable1 testCallable1 = new TestCallable1();for (int i = 0; i < 5; i++) {// 调用 Callable 任务只能用这一种方法Future<Integer> submit = executorService.submit(testCallable1);System.out.println("返回值:" + submit.get());}} }class TestRunnable1 implements Runnable {@Overridepublic void run() {System.out.println("我是 Runnable 任务,调用我的线程是:" + Thread.currentThread().getName());} }class TestCallable1 implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("我是 Callable 任务,调用我的线程是:" + Thread.currentThread().getName());return 666;} }
newCachedThreadPool()
该方法构建的线程池会立即执行任务,如果当前存在空闲线程,则直接执行任务;如果当前不存在空闲线程,则创建一个新线程执行任务。在该线程池内的空闲线程只会保留 60 秒。
public class MyCachedThreadPool {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newCachedThreadPool();// 调用 Runnable 任务TestRunnable2 testRunnable2 = new TestRunnable2();for (int i = 0; i < 5; i++) {// 调用 Runnable 任务可以用以下两种方法,二者的区别在于前者没返回值,后者有返回值executorService.execute(testRunnable2);Future<?> submit = executorService.submit(testRunnable2);}// 调用 Callable 任务TestCallable2 testCallable2 = new TestCallable2();for (int i = 0; i < 5; i++) {// 调用 Callable 任务只能用这一种方法Future<Integer> submit = executorService.submit(testCallable2);System.out.println("返回值:" + submit.get());}} }class TestRunnable2 implements Runnable {@Overridepublic void run() {System.out.println("我是 Runnable 任务,调用我的线程是:" + Thread.currentThread().getName());} }class TestCallable2 implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("我是 Callable 任务,调用我的线程是:" + Thread.currentThread().getName());return 666;} }
newSingleThreadExecutor()
该方法构建的线程池只存在一个线程,会顺序地执行所提交的任务。
public class MySingleThreadExecutor {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newSingleThreadExecutor();// 调用 Runnable 任务TestRunnable3 testRunnable3 = new TestRunnable3();for (int i = 0; i < 5; i++) {// 调用 Runnable 任务可以用以下两种方法,二者的区别在于前者没返回值,后者有返回值executorService.execute(testRunnable3);Future<?> submit = executorService.submit(testRunnable3);}// 调用 Callable 任务TestCallable3 testCallable3 = new TestCallable3();for (int i = 0; i < 5; i++) {// 调用 Callable 任务只能用这一种方法Future<Integer> submit = executorService.submit(testCallable3);System.out.println("返回值:" + submit.get());}} }class TestRunnable3 implements Runnable {@Overridepublic void run() {System.out.println("我是 Runnable 任务,调用我的线程是:" + Thread.currentThread().getName());} }class TestCallable3 implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("我是 Callable 任务,调用我的线程是:" + Thread.currentThread().getName());return 666;} }
Java 实现多线程的四种方式相关推荐
- Java 实现多线程的四种方式 超详细
Java 实现多线程的四种方式 文章目录 Java 实现多线程的四种方式 一.继承 Thread 类 二.实现 Runnable 接口 三.实现 Callable 接口 四.线程池 1,Executo ...
- java创建多线程的四种方式
java多线程的创建方式是面试经常会被问到的一个问题,因此在这里我对java创建多线程的四种方式做一个简单的归纳与总结,便于复习. 一.继承Thread类创建多线程 ① 创建一个继承于Thread类的 ...
- 创建多线程的四种方式
创建多线程的四种方式 方式一:继承于Thread类 创建一个继承于Thread类的子类 重写Thread类的run()->将此线程执行的操作声明在run()中 创建Thread类的子类的对象 通 ...
- Java中创建对象的四种方式
为什么80%的码农都做不了架构师?>>> Java中创建对象的四种方式 (1) 用new语句创建对象,这是最常见的创建对象的方法. (2) 运用反射手段,调用java.l ...
- Java 创建类的四种方式
Java 创建类的四种方式 对于上学期已经学习过c++的同学,是不是对另一大编程语言产生了浓厚的兴趣,对于c++的面向对象编程,又和java的面向变量有何区别,下面我们从java四种创建对象来说起. ...
- java怎样输入五个数字打一成语,Java的线程安全四种方式五个等级[1]
Java的线程安全四种方式五个等级[1]以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 四种方式 sychronized ...
- android java 多线程,Android多线程的四种方式
当我们启动一个App的时候,Android系统会启动一个Linux Process,该Process包含一个Thread,称为UI Thread或Main Thread.通常一个应用的所有组件都运行在 ...
- java中创建对象的方式有哪些,Java中创建对象的四种方式
四种方式: http://wenku.baidu.com/link?url=mv6VbMd3d-aCkbGMhn6rbLwFbef7v60nRbyA-thP6Y7hqtjiv0K0_kdtfOWaUj ...
- JAVA实现多线程的三种方式
在Java中可通过三种方式来实现多线程: 1.继承Thread类,重写run( )方法 2.实现Runnable接口,重写run( )方法 3.实现Callable接口,重写call( )方法并使用F ...
最新文章
- Java Web 中的一些问题
- 入职体检体检错了_我们如何更新入职体验并获得更多用户
- 谋定国家5G战略的基石-工信部韦乐平:经信研究网络领先
- Java List.size()方法:返回列表中元素的个数(亲测)
- 关于request.getServletPath(),request.getContextPath()的总结
- CompSNN: A Lightweight Spiking Neural Network Based on Spatiotemporally Compressive Spike Features
- Win32Asm学习笔记[不断更新]
- MFC选择目录和多个文件
- 论文画图软件(转载)
- 特种作业2021年电力电缆考试题库
- 月入1W+的自媒体达人都会用到的运营工具
- b站的视频如何下载到手机上
- vue 微信分享至朋友圈分享至朋友代码封装
- 极光:2019年个人网盘行业研究报告
- 剪切caspase3_Proteintech抗体检测caspase 3前体及剪切体 - 泽浩公司
- python生成10个随机密码_python题:随机密码生成。编写程序,在26个字母大小写和9个数字组成的列表中随机生成10个8位密码...
- jQuery实现弹幕效果(鼠标单击和键盘回车键)
- VB快速注销/重启/关闭计算机
- ensp安装包以及虚拟USG6000V防火墙
- 较为详细的记录总结TensorRT的python接口的使用,环境配置,模型转换和静态动态模型推理
热门文章
- 小程序流量主赚广告费,选择抖音快手微信QQ哪个平台好?教你理清思路
- 复制互联网2010全球最值得模仿的230个网站
- 虚拟机与主机之间通信
- 网站是服务器备案还是域名备案,做一个网站域名先注册还是备案
- PHP无用图片清理,DNUI Delete not used image无用图片删除插件介绍及使用方法 | 很文博客...
- Flink流处理核心编程
- Processing入门教程
- Unity2D横版游戏开发(一) 人物的移动和跳跃
- 用SeismicUnix(SU)读segy文件画地震剖面
- 练手|常见26种NLP任务的练手项目