Java 线程对比(Thread,Runnable,Callable)实例详解
作者:android_小路 发布时间:2022-10-20 21:34:08
Java 线程对比Thread,Runnable,Callable
java 使用 Thread 类代表线程,所有现场对象都必须是 Thread 类或者其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流。java 使用线程执行体来代表这段程序流。
1.继承Thread 类创建线程
启动多线程的步骤如下:
(1)定义Thread 类的子类,并重写该类的run() 方法,该run() 方法的方法体就代表类线程需要完成的任务。因此把run() 方法称为线程执行体。
(2)创建 Thread 子类的实例,即创建线程对象。
(3)调用线程的star()方法来启动该线程。
相关代码如下:
/**
* 继承 thread 的内部类,以买票例子
*/
public class FirstThread extends Thread{
private int i;
private int ticket = 10;
@Override
public void run() {
for (;i<20;i++) {
//当继承thread 时,直接使用this 可以获取当前的线程,getName() 获取当前线程的名字
// Log.d(TAG,getName()+" "+i);
if(this.ticket>0){
Log.e(TAG, getName() + ", 卖票:ticket=" + ticket--);
}
}
}
}
private void starTicketThread(){
Log.d(TAG,"starTicketThread, "+Thread.currentThread().getName());
FirstThread thread1 = new FirstThread();
FirstThread thread2 = new FirstThread();
FirstThread thread3 = new FirstThread();
thread1.start();
thread2.start();
thread3.start();
//开启3个线程进行买票,每个线程都卖了10张,总共就30张票
}
运行结果:
可以看到 3 个线程输入的 票数变量不连续,注意:ticket 是 FirstThread 的实例属性,而不是局部变量,但是因为程序每次创建线程对象都需要创建一个FirstThread 的对象,所有多个线程不共享该实例的属性。
2.实现 Runnable 接口创建线程
注意:public class Thread implements Runnable
(1)定义 Runnable 接口的实现类,并重写该接口的run()方法,该run() 方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable 实例类的实例,此实例作为 Thread 的 target 来创建Thread 对象,该Thread 对象才是真正的对象。
相关代码如下:
/**
* 实现 runnable 接口,创建线程类
*/
public class SecondThread implements Runnable{
private int i;
private int ticket = 100;
@Override
public void run() {
for (;i<20;i++) {
//如果线程类实现 runnable 接口
//获取当前的线程,只能用 Thread.currentThread() 获取当前的线程名
Log.d(TAG,Thread.currentThread().getName()+" "+i);
if(this.ticket>0){
Log.e(TAG, Thread.currentThread().getName() + ", 卖票:ticket=" + ticket--);
}
}
}
}
private void starTicketThread2(){
Log.d(TAG,"starTicketThread2, "+Thread.currentThread().getName());
SecondThread secondThread = new SecondThread();
//通过new Thread(target,name)创建新的线程
new Thread(secondThread,"买票人1").start();
new Thread(secondThread,"买票人2").start();
new Thread(secondThread,"买票人3").start();
//虽然是开启了3个线程,但是一共只买了100张票
}
运行结果:
可以看到 3 个线程输入的 票数变量是连续的,采用 Runnable 接口的方式创建多个线程可以共享线程类的实例的属性。这是因为在这种方式下,程序所创建的Runnable 对象只是线程的 target ,而多个线程可以共享同一个 target,所以多个线程可以共享同一个线程类(实际上应该是该线程的target 类)的实例属性。
3.使用 Callable 和Future 创建线程
从 java 5 开始,Java 提供了 Callable 接口,该接口是runnable 的增强版,Callable 提供类一个 call() 方法可以作为线程执行体,但是call() 方法的功能更强大。
(1) call() 方法可以有返回值
(2) call() 方法可以声明抛出异常
因此我们完全可以提供一个callable 对象作为Thread的 target ,而该线程的执行体就是该callable 对象的call() 方法。同时 java 5 提供了 Future 接口 来代表Callable 接口里 call() 方法的返回值,并且提供了一个 futureTask 的实现类,该实现类实现类 future 接口,并实现了runnable 接口—可以作为Thread 类的target.
启动步骤如下:
(1)创建callable接口的实现类,并实现call() 方法,该call() 方法将作为线程的执行体,且该call() 方法是有返回值的。
(2)创建 callable实现类的实例,使用 FutureTask 类来包装Callable对象,该FutureTask 对象封装 call() 方法的返回值。
(3)使用FutureTask 对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获取子线程执行结束后的返回值。
相关代码如下:
/**
* 使用callable 来实现线程类
*/
public class ThirdThread implements Callable<Integer>{
private int ticket = 20;
@Override
public Integer call(){
for ( int i = 0;i<10;i++) {
//获取当前的线程,只能用 Thread.currentThread() 获取当前的线程名
// Log.d(TAG,Thread.currentThread().getName()+" "+i);
if(this.ticket>0){
Log.e(TAG, Thread.currentThread().getName() + ", 卖票:ticket=" + ticket--);
}
}
return ticket;
}
}
private void starCallableThread(){
ThirdThread thirdThread = new ThirdThread();
FutureTask<Integer> task = new FutureTask<Integer>(thirdThread);
new Thread(task,"有返回值的线程").start();
try {
Integer integer = task.get();
Log.d(TAG,"starCallableThread, 子线程的返回值="+integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
运行结果:
注意:Callable的call() 方法允许声明抛出异常,并且允许带有返回值。
程序最后调用FutureTask 对象的get()方法来返回Call()方法的返回值,导致主线程被阻塞,直到call()方法结束并返回为止。
4.三种方式的对比
采用继承Thread 类的方式创建多线程
劣势: 已经继承Thread类不能再继承其他父类。
优势: 编写简单
采用继承Runnable,Callable 接口的方式创建多线程
劣势: 编程稍微有点复杂,如果需要访问当前线程必须使用Thread.currentThread()
优势:
(1)还可以继承其他类
(2)多个线程可以共享一个target 对象,所以非常适合多个相同的线程来处理同一份资源的情况,从而将cpu,代码和数据分开,形成清晰的模型,较好的体现类面向对象的思想。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
来源:http://blog.csdn.net/android_freshman/article/details/53787011
猜你喜欢
- 什么是泛型Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了 编译时类型安全监测机制,该机制允许我们在编译时检测到非法
- 一、对AOP的初印象首先先给出一段比较专业的术语:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向
- 1 前言为什么我们在使用SpringBoot框架开发Java Web应用需要引入大量的starter?例如,我们引入Redis就在Maven
- 前言现在是移动端产品疯狂的年代,随之,移动端支付也是热门小技能,最近本公司在做一个移动端,要接入微信支付和支付宝支付, * 惯,功能做完之后做
- Servlet:在Servlet中拼接html内容JSP:在html中拼接javaJSP+JavaBean:利用javaBean将大量的代码
- springboot logback动态获取application的配置项在多环境的情况下,logback的日志路径需要进行针对性配置,也就
- 重写java object类的equals方法覆盖equals方法请遵守约定什么情况下要覆盖equals方法容易违反的对称性不易察觉的传递性
- 本文实例为大家分享了闪耀字体效果的具体代码,供大家参考,具体内容如下import android.content.Context;impor
- 一、目的针对不同地区,设置不同的语言信息。SpringBoot国际化配置文件默认放在classpath:message.properties
- 一.工程文件二.Main.java主函数,实现类package ui;//主函数实现public class Main { &
- 前言本次示例代码的文件结构如下图所示。1. 导入依赖坐标在 order-service 的 pom.xml 文件中导入 Feign 的依赖坐
- 前言其实不管是哪种滑动方式,基本思想都是类似的:当点击事件传递到View时,系统记下触摸点的坐标,手指移动的时候,系统记下移动后的坐标,并计
- 实例如下:package com.bwsk.modules.weixin.util;import java.util.Random;/**
- 摘要:在spring boot中 MVC这部分也有默认自动配置,也就是说我们不用做任何配置,那么也是OK的,这个配置类就是 WebMvcAu
- 在学习安卓的最初过程中我们学的都是最基本的一个活动,只有一个活动的应用也太简单了吧,没错我们的最求应该更高点,不管你创建多少个活动,接下里我
- 本文实例为大家分享了Android实现记住账号密码的具体代码,供大家参考,具体内容如下布局一个复选框<CheckBox
- 有些人可能对线程池比较陌生,并且更不熟悉线程池的工作原理。所以他们在使用线程的时候,多数情况下都是new Thread来实现多线程。但是,往
- 目录类划分时关于内聚性的问题静态类的设计高内聚类的设计附:面向过程编程中模块的内聚性偶然内聚或巧合内聚(Coincidental)逻辑内聚(
- 最新Android版本、API级别与代号对应关系代号版本号API/NDK级别发布时间-O8.0API level 262017-3-21牛轧
- SpringBoot 集成 activiti 基础环境搭建添加依赖<dependency> <groupId&g