Java Runnable和Thread实现多线程哪个更好你知道吗
实现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去创建异步执行任务。
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
相关文章
SpringBoot @PathVariable使用时遇到的问题及解决
这篇文章主要介绍了SpringBoot @PathVariable使用时遇到的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-10-10windows如何使用bat脚本后台启动/停止和重启jar包服务
这篇文章主要介绍了windows使用bat脚本后台启动/停止和重启jar包服务的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-11-11Spring Cloud Gateway集成Sentinel流控详情
这篇文章主要介绍了Spring Cloud Gateway集成Sentinel流控详情,Sentinel支持对Spring Cloud Gateway、Zuul等主流的API Gateway进行限流,需要的朋友可以参考一下2022-09-09
最新评论