Java Runnable和Thread实现多线程哪个更好你知道吗
作者:小小茶花女 发布时间:2021-08-17 05:48:50
实现Runnable 接口比继承Thread 类的方式更好:
(1)可以避免由于Java单继承带来的局限性;
(2)可以实现业务执行逻辑和数据资源的分离;
(3)可以与线程池配合使用,从而管理线程的生命周期;
1. 避免由于Java单继承带来的局限性
如果异步逻辑所在类已经继承了一个基类,就没有办法再继承Thread类。比如,当一个Dog类继承了Pet类,再要继承Thread类就不行了。所以在已经存在继承关系的情况下,只能使用实现Runnable接口的方式。
public class ThreadTask extends Thread {
// 线程的执行体
@Override
public void run() {
System.out.println("线程执行的任务");
}
}
public class RunnableTask implements Runnable {
// 线程的执行体
@Override
public void run() {
System.out.println("线程执行的任务");
}
}
2. 可以实现业务执行逻辑和数据资源的分离
逻辑和数据更好分离。通过实现Runnable接口的方法创建多线程更加适合同一个资源被多段业务逻辑并行处理的场景。在同一个资源被多个线程逻辑异步、并行处理的场景中,通过实现Runnable接口的方式设计多个target执行目标类可以更加方便、清晰地将执行逻辑和数据存储分离,更好地体现了面向对象的设计思想。
注意:并不是继承Thread类不能实现资源共享,而是没有实现Runnable接口更方便,更清晰,他们两的主要区别就是类和接口的区别,但是我们一般多用Runnable。
(1) 通过继承Thread类的方式实现多线程,数据资源和业务执行逻辑是耦合在一起的, 多个线程并发地完成各自的任务,访问各自的数据资源,而不是共享一份数据资源:
public class ThreadDemo extends Thread {
// 数据资源
private int ticket = 3;
// 业务执行逻辑
@Override
public void run() {
for(int i=0;i<3;i++){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+" 卖票--->"+ ticket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(Thread.currentThread().getName()+" 线程运行结束");
}
public static void main(String[] args) throws InterruptedException {
// 创建2个线程,分别去执行线程体中的业务逻辑
Thread thread1 = new ThreadDemo();
thread1.start();
Thread thread2 = new ThreadDemo();
thread2.start();
Thread.sleep(1000);
System.out.println("main线程运行结束");
}
}
多个线程并发地完成各自的任务,访问各自的数据资源:
Thread-0 卖票--->3
Thread-1 卖票--->3
main线程运行结束
Thread-0 卖票--->2
Thread-1 卖票--->2
Thread-1 卖票--->1
Thread-0 卖票--->1
Thread-0 线程运行结束
Thread-1 线程运行结束
(2) 通过继承Thread类可以实现资源共享:
public class ThreadTask {
private int ticket = 3;
public synchronized void saleTicket(){
for(int i=0;i<3;i++){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+" 卖票--->"+ticket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(Thread.currentThread().getName()+" 线程运行结束");
}
}
public class ThreadA extends Thread {
ThreadTask threadTask ;
public ThreadA(ThreadTask threadTask){
super();
this.threadTask = threadTask;
}
@Override
public void run() {
threadTask.saleTicket();
}
}
public class ThreadB extends Thread {
ThreadTask threadTask ;
public ThreadB(ThreadTask threadTask){
super();
this.threadTask = threadTask;
}
@Override
public void run() {
threadTask.saleTicket();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadTask threadTask = new ThreadTask();
ThreadA t1 = new ThreadA(threadTask);
ThreadB t2 = new ThreadB(threadTask);
t1.start();
t2.start();
}
}
执行结果:
Thread-0 卖票--->3
Thread-1 卖票--->2
Thread-1 卖票--->1
Thread-0 线程运行结束
Thread-1 线程运行结束
(3) 通过实现Runnable接口实现多线程,能更好地做到多个线程并发地完成同一个任务,访问同一份数据资源。多个线程的代码逻辑可以方便地访问和处理同一个共享数据资源 ,这样可以将线程逻辑和业务数据进行有效的分离,更好地体现了面向对象的设计思想。
public class RunnableDemo{
public static class RunnableTask implements Runnable{
// 数据资源
private int ticket = 3;
// 线程执行体
@Override
public synchronized void run() {
for(int i=0;i<3;i++){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+" 卖票--->"+ticket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(Thread.currentThread().getName()+" 线程运行结束");
}
}
public static void main(String[] args) {
// 将这一个target作为参数传给两个线程,那么这两个线程执行的都是这个target的run()方法
Runnable target = new RunnableTask();
// 创建两个线程执行target的线程体
Thread thread1 = new Thread(target,"thread1");
thread1.start();
Thread thread2 = new Thread(target,"thread2");
thread2.start();
System.out.println("main线程运行结束");
}
}
多个线程并发地完成同一个任务,访问同一份数据资源:
main线程运行结束
thread1 卖票--->3
thread1 卖票--->2
thread1 卖票--->1
thread1 线程运行结束
thread2 线程运行结束
3. 可以与线程池配合使用,从而管理线程的生命周期
实现Runnable接口来实现线程,执行目标类,更容易和线程池配合使用,异步执行任务在大多数情况下是通过线程池去提交的,而很少通过创建一个新的线程去提交,所以更多的做法是,通过实现Runnable接口创建异步执行任务,而不是继承Thread去创建异步执行任务。
来源:https://hengheng.blog.csdn.net/article/details/123008089
猜你喜欢
- 最近项目需要通过经纬度查询 具体的地址和区域名称,通过查询网络资源,发现提供的大多是得到具体的地址而对区域或城市名称的获取就不是很好把握;在
- 有小伙伴表示微人事(https://github.com/lenve/vhr)的权限粒度不够细。不过松哥想说的是,技术都是相通的,明白了 v
- java对字符串进行utf-8编码我们在调用第三方 API 时,常常会被要求用到路径变量,而路径变量一般都是 utf-8 编码的,因此需要对
- 问题用过storm或者jstorm的都知道,如果在bolt代码中发生了没被catch住的异常,所在worker进程会退出。本文就从源码角度分
- KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普
- 本文实例为大家分享了java实现扫雷游戏入门程序的具体代码,供大家参考,具体内容如下分析:1.首先布一个10*10的雷阵,即二维数组map,
- 1,实现效果 2,实现代码:【1】 shape_drawable.xml 
- 本文实例分析了C#中Convert.ToString和ToString的区别,对于初学者来说是很有必要加以熟练掌握的。具体分析如下:1.Co
- Java 实现 Http Server,模拟前端接口调用前言: 最近看到一个很有意思的东西,手写简单的 Http Server,而且只需要使
- GC,Garbage Collect,中文意思就是垃圾回收,指的是系统中的内存的分配和回收管理。其对系统性能的影响是不可小觑的。今天就来说一
- 前提:集成开发环境(IDE):eclipsejdk版本:8.0File类的几个方法:1)isFile()测试此抽象路径名表示的文件是否为普通
- 在封装中有一种特殊的类,能够把基本的数据类型进行转换来方便实际的使用。我们在之前提到的一些数据类型,最明显的特征是所有字母为小写状态,那么经
- 前言基于 C# 的 图表控件库 ScottPlot,开源免费,可以用于开发一些上位机软件,如电压、电流波形的显示,开发【示波器】图形界面,可
- 本文实例为大家分享了Android绝对布局AbsoluteLayout的具体代码,供大家参考,具体内容如下1>AbsoluteLayo
- 公司产品需要一个雷达图来展示各维度的比重,网上找了一波,学到不少,直接自己上手来撸一记无图言虚空简单分析一波,确定雷达图正几边形的--正五边
- 前言:由于公司的业务,硬生生的把ios开发的我,掰成了android!关于上传文件的需求处理,做了一个Java的简单封装 DocumentM
- 本文实例讲述了android获取当前运行Activity名字的方法,可以避免即时聊天再出现通知的情况。分享给大家供大家参考。具体方法如下:最
- 本文实例讲述的是AlertDialog,这种对话框会经常遇到。AlertDialog跟WIN32开发中的Dialog不一样,AlertDia
- this总要有个事物来代表类的当前对象,就像C++中的this指针一样,Java中的this关键字就是代表当前对象的引用。它有三个主要的作用
- 本文实例讲述了Java设计模式之模板方法模式。分享给大家供大家参考,具体如下:我们在生活中,很多事情都包含特定的一些步骤。如去银行办理业务,