在Java中,实现多线程的方式有3种,分别是继承Thread类、实现Runnable接口、实现Callable接口。关于它们是如何实现,有何区别呢?

方式一:继承Thread类

一个普通类只要继承了传说中的Thread类,那么,这个普通类就具备了多线程操作能力。

But,这个普通类必须重写Thread类中的run()方法,因为我们所编写的代码要放在这个方法中,而且所编写的代码是一个线程体。

通过继承Thread类实现多线程实例:

MyThread类(普通类)
package cn.tkr.thread;public class MyThread extends Thread {//重写Thread类中的run()方法@Overridepublic void run() {//编写线程体(线程要执行的代码)System.out.println("大家好,我是MyThread类中的run()方法中的代码");}
}

TestMyThread类(测试类)

package cn.tkr.thread;public class TestMyThread {public static void main(String[] args) {MyThread m = new MyThread();  //创建线程类的对象m.start();  //启动线程System.out.println("我是main方法中的代码");}
}

实例分析:

在运行这个程序时,完美的启动了Java虚拟机,负责执行主线程(主方法)中的代码,在执行MyThread m = new MyThread()之前有一个线程,线程名称为main,是主线程,我们使用m.start()启动线程,这个时候就有两个线程了,一个是主线程,一个是m线程。

要注意一点,由于哪个线程先“抢占”到CPU资源不确定,所以这两个线程的哪个先执行也是不确定的。

方式二:实现Runnable接口

一个普通类实现了Runnable接口,那么,这个普通类也就具备了多线程操作能力。

But,Runnable接口中没有定义start()方法,所以想要启动线程,我们还得拜托一下Thread类,毕竟人家有start()方法。

通过实现Runnable接口实现多线程实例:

MyRunnable类(普通类)
package cn.tkr.thread;public class MyRunnable implements Runnable {//重写Runnable接口中的run()方法@Overridepublic void run() {for (int i = 0; i < 3; i++) {System.out.println("我是MyRunnable类中的run方法中的代码" + i);}}
}

TestMyRunnable类(测试类)

package cn.tkr.thread;public class TestMyRunnable {public static void main(String[] args) {MyRunnable mr = new MyRunnable();   //创建线程类的对象Thread m = new Thread(mr);m.start();for (int i = 0; i < 3; i++) {System.out.println("我是main方法中的代码" + i);}}
}

运行结果:

我是main方法中的代码0
我是MyRunnable类中的run方法中的代码0
我是MyRunnable类中的run方法中的代码1
我是MyRunnable类中的run方法中的代码2
我是main方法中的代码1
我是main方法中的代码2

实例分析:

在Thread类中有个构造方法叫Thread(Runable target ),专门用来接收线程类的对象(实现Runnable接口的线程类对象),把mr线程传递给Thread类之后,我们就可以通过对象m来完美地启动线程。

方式三:实现Callable接口

JDK1.5之后,冒出了一个叫Callable的接口,它的到来,让Runnable抑郁了,因为Callable太强大了。

与Runnable实现多线程相比,Callable支持泛型,call()方法可以有返回值,而且还支持泛型的返回值,比run()方法更强大的一点是,居然还能抛出异常。

But,Callable接口中的Call()方法需要借助FutureTask类获取结果。

FutureTask类是RunnableFuture接口的实现类,而RunnableFuture接口又继承了Future接口和Runnable接口,所以FutureTask类也是Runnable接口的实现类。

注:FutureTask类是一个任务管理器类。

总的来说,这种方式是通过创建FutureTask类的对象将Callable接口的实现类传入,从而实现多线程。

通过实现Callable接口实现多线程实例:

RandomCallable类(普通类)

package cn.tkr.thread;import java.util.concurrent.Callable;public class RandomCallable implements Callable<String> {@Overridepublic String call() throws Exception {String[] str = {"A","B","C","D","E"};   //创建一个数组,长度为5int random = (int)(Math.random()*4)+1;  //产生一个1~4之间的随机数return str[random]; //根据产生的随机数返回数组中对应位置的字符串}
}

TestRandomCallable类(测试类)

package cn.tkr.thread;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class TestRandomCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {RandomCallable rc = new RandomCallable();   //创建一个任务FutureTask<String> ft = new FutureTask<>(rc);   //创建一个任务管理器Thread t = new Thread(ft);System.out.println("任务是否已完成:" + ft.isDone());t.start();System.out.println(ft.get());System.out.println("任务是否已完成:" + ft.isDone());}
}

运行结果:

任务是否已完成:false
B
任务是否已完成:true

实例分析:

FutureTask类是Runnable接口的实现类,所以要启动线程我们还是要请Thread类来帮忙。

在使用start()方法之前,任务是没有完成的,所以isDone()的结果为false,当我们开启线程后,用get()方法获取到了结果,说明任务已完成,因为只有结果出来任务才能结束,否则无论get()方法后面有多少句代码都不会执行的。

继承Thread类与实现Runnable接口的区别

大家都知道,Java类具有单继承的特点,如果一个类选择继承Thread类来实现多线程,那么这个类就不能再继承其他类了。

但是如果选择实现Runnable接口,不仅避免了单继承带来的局限性,还能方便共享资源,同一个资源可以有多个代理访问。

实例(三个窗口卖票问题):

在火车站的售票厅里,有三个窗口在卖5张票,每个窗口有30人在排队,那到底要使用哪种方式实现多线程呢?

第一种,使用继承Thread类的方式来实现

实例:

TicketThread类

package cn.tkr.thread;public class TicketThread extends Thread {private int tickets = 5;public TicketThread (String name){super(name);}@Overridepublic void run() {for (int i = 0; i < 30; i++) {if (tickets > 0){System.out.println(super.getName() + "卖第" + (tickets--) + "张票");}}}
}

TestTicketThread类(测试类)

package cn.tkr.thread;public class TestTicketThread {public static void main(String[] args) {//创建三个线程类的对象TicketThread t1 = new TicketThread("窗口一");TicketThread t2 = new TicketThread("窗口二");TicketThread t3 = new TicketThread("窗口三");t1.start();t2.start();t3.start();}
}

运行结果:

窗口二卖第5张票
窗口三卖第5张票
窗口一卖第5张票
窗口三卖第4张票
窗口二卖第4张票
窗口三卖第3张票
窗口一卖第4张票
窗口三卖第2张票
窗口二卖第3张票
窗口三卖第1张票
窗口一卖第3张票
窗口一卖第2张票
窗口二卖第2张票
窗口一卖第1张票
窗口二卖第1张票

实例分析:

三个窗口同时卖票,我们就要创建三个线程。从运行结果看,本来只卖5张票,现在变成了15张了,很显然,private int tickets = 5这个资源并没有共享。

这三个不同的线程类的对象,所用到的资源都是相互独立的,也就是说线程类对象t1、t2、t3用到的都是private int tickets = 5这个资源,所以就会出现15张票。

再看另一种,使用实现Runnable接口的方式来实现

实例:

TicketRunnable类

package cn.tkr.thread;public class TicketRunnable implements Runnable {private int tickets = 5;@Overridepublic void run() {for (int i = 0; i < 30; i++) {if (tickets > 0){System.out.println(Thread.currentThread().getName() + "卖第" + (tickets--) + "张票");}}}
}

TestTicketRunnable类(测试类)

package cn.tkr.thread;public class TestTicketRunnable {public static void main(String[] args) {//创建线程类的对象TicketRunnable tr = new TicketRunnable();Thread t1 = new Thread(tr,"窗口一");Thread t2 = new Thread(tr,"窗口二");Thread t3 = new Thread(tr,"窗口三");t1.start();t2.start();t3.start();}
}

运行结果:

窗口一卖第5张票
窗口二卖第4张票
窗口一卖第3张票
窗口二卖第2张票
窗口一卖第1张票

实例分析:

通过实现Runnable接口的方式实现多线程,达到了多个线程共享同一个资源的目的,完美地解决了使用Thread类来实现多线程出现的问题。

感谢你看到这里,我是程序员麦冬,一个java开发从业者,深耕行业六年了,每天都会分享java相关技术文章或行业资讯

欢迎大家关注和转发文章,后期还有福利赠送!

java实现多线程抢单_来聊一聊3种实现JAVA多线程的方式吧相关推荐

  1. java 静态内部类 线程安全问题_单例模式的七种写法, 面试题:线程安全的单例模式...

    http://cantellow.iteye.com/blog/838473 http://meizhi.iteye.com/blog/537563 第一种(懒汉,线程不安全): Java代码  pu ...

  2. java语言程序设计你_清华大学出版社-图书详情-《Java语言程序设计》

    前言 Java语言是一种典型的面向对象的.跨平台的.支持分布式和多线程的优秀编程语言,具有极强的扩展性.自其诞生以来,迅速被业界认可并广泛应用于Web应用程序的开发中.在此形势下,国内高校在计算机及相 ...

  3. java常见性能优化_十大最常见的Java性能问题

    java常见性能优化 Java性能是所有Java应用程序开发人员都关心的问题,因为快速使应用程序与使其正常运行同等重要. 史蒂文·海恩斯(Steven Haines)使用他在Java性能问题上的个人经 ...

  4. 中秋节图案 用java代码打出来_这个中秋,我用 Java 画了一个月饼!

    栈长代表微信公众号 "Java技术栈" 祝所有粉丝中秋佳节快乐! 为了用一种特殊的方式表达我的心意,去年中秋节,我写了这篇文章: 没错,去年一天,我学了 20 种编程语言,刺激.. ...

  5. java生成pdf图表_开发员指南:使用Java图表转换为PDF/JPG等图像

    Aspose.Cells for JavaExcel电子表格处理API,它允许Java开发人员在自己的Java应用程序中嵌入可读取.写入和操作Excel电子表格的能力,而无需依赖Microsoft E ...

  6. java泛型 简书_一文带你认识Java泛型基础

    Java泛型基础 1. 认识泛型 泛型是在JDK1.5之后增加的新功能. 泛型可以解决数据的安全性问题, 主要的原理是在类声明的时候通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型. ...

  7. mac上java文件如何编译_如何在Mac上用Java编译和运行程序?

    小编典典 在Mac OSX或任何主要操作系统上编译和运行Java应用程序非常容易.Apple随OSX一起提供了一个功能齐全的Java运行时和开发环境,因此您要做的就是编写Java程序并使用内置工具来编 ...

  8. java编程最新图书_清华大学出版社-图书详情-《Java程序设计》

    前言 Java是一种完全面向对象的程序设计语言,具有卓越的通用性.高效性.平台移植性和安全性,得到广泛的应用.在全球云计算和移动互联网产业高速发展的环境下,Java具备显著的优势和广阔前景.本书以Ja ...

  9. java自动推断类型_推断:Facebook的新Java静态分析工具

    java自动推断类型 如何使用Facebook的Infer改善Java开发工作流程? 如果您与技术话题保持同步(如果您正在阅读此博客,我想您会这样做),那么您可能听说过Facebook 刚刚向公众发布 ...

最新文章

  1. python3.8.0shell_Python 3.8.0 正式发布 更新内容
  2. Vue项目实操cookie相关操作封装
  3. [转载]什么是似然度~maximum likelihood
  4. 怎样复制粘贴windows命令行中的内容
  5. k-core与k-shell的区别
  6. 使用yum查看安装了哪些软件包、某软件包是否已经安装
  7. python函数每日一讲 - cmp(x,y)
  8. 【最小生成树】水箱(P5952)
  9. next_permutation算法(基于交换)
  10. matlab GUI 设计 自学笔记
  11. 部署web应用程序到tomcat
  12. JDG战队与英特尔达成为期两年的冠名合作
  13. 有限项加和的极限求解思路定式
  14. Atitit 提升开发效率 提升团队人员能力 目录 1. 多语言扩展 提升抽象度 2 2. 从上到下法 vs 从下倒上 问题诊断解决法 2 2.1. 培训机制 上到下法 2 2.2. 问题案例
  15. 美式期权损益图matlab,期权损益图的画法
  16. Android获取地理坐标,Android 通过经纬度获取地理位置信息
  17. Linux 下Nginx SSL/HTTPS 配置
  18. RStudio打开后空白的解决
  19. python统计不同字符的个数
  20. 短文本分类:电力95598工单分类实现

热门文章

  1. 如何删除“无法读源文件或磁盘”的空头文件
  2. iOS汉字转拼音,日韩文字转拼音
  3. easyui---基础组件:window
  4. initrd in linux 2.6.32.27
  5. python logging详解及自动添加上下文信息
  6. Intellij Idea下tomcat设置自动编译
  7. 20161120-安全测试
  8. ZOJ 3817Chinese Knot(The 2014 ACM-ICPC Asia Mudanjiang Regional First Round)
  9. SQLSTATE[HY000] [2013] Lost connection to MySQL...
  10. 多角度解读优酷土豆合并的深意