002-线程实现方式【thread、runnable、callale、thread和runnable对比】
一、概述
1、实现方式
在java中对于多线程实现一定要有一个线程的主类,而这个线程的主类往往是需要操作一些资源,但是对于多线程主类的实现是:
继承Thread父类
从java的Thread类继承实现多线程,也是实现其run方法,然后声明实例,并调用实例的start方法启动线程。
实现Runnable接口(Callable接口)
使用Runnable接口实现多线程需要两个步骤,首先实现Runnable接口类,然后声明Thread实例,调用thread实例的start方法,开始执行。
2、java Thread类的主要方法介绍
Thread的实例方法:
方法定义 |
方法说明 |
public void start() |
最常用的方法,顾名思义启动线程,即开始执行线程的run方法 |
public void run() |
如果线程重写了run方法,那么执行重写的方法,否则执行线程的Runnable接口中定义的run方法 |
public final void setName(String) |
设置线程的名称 |
public final void setPriority(int) |
设置线程的优先级(范围在1-10包含1,10) |
public final void setDeamon(boolean) |
设置线程是否是后台线程 |
public final void join(long) |
在另外一个线程中调用当前线程的join方法,会导致当前线程阻塞,直到另一线程执行完毕,或者超过参数指定毫秒数 |
public void interrupt() |
中断线程 |
public final boolean isAlive() |
线程是否处于存活状态,线程在启动和结束之前都处于存活状态 |
Thread类的常用静态方法:
方法定义 |
方法说明 |
public static void yield() |
使当前运行线程相同优先级的线程获得执行机会,类似sleep,但是只会将cpu让给相同优先级的线程 |
public static void sleep(long) |
使当前线程休眠指定毫秒的时间 |
public static boolean holdsLock(Object x) |
判断当前线程是否拥有对象的锁 |
public static Thread currentThread() |
获得当前线程实例 |
public static void dumpStack() |
打印当前线程的执行堆栈,这对多线程程序的调试很有帮助 |
二、继承Thread类
java.lang.Thread,子类继承Thread,之后覆写run方法,线程主方法
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {// 主方法
for (int i = 0; i < 10; i++) {
System.out.println(this.name + " i:" + i);
}
}
}
需要注意,所有线程都是编发执行,在某个时间段上会有多个线程交替执行。所以为了达到这样的目的,绝对不能够直接调用run方法,而是调用线程对象中的start方法
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
MyThread mt3 = new MyThread("线程C");
//mt1.run() 此时 就是方法调用顺序执行
mt1.start();
mt2.start();
mt3.start();
思考?为什么多线程启动不用run,而用start方法?源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException(); //异常说明
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
源码解析:
1、throw new IllegalThreadStateException(); //异常说明
是Runtime异常,多次启动会抛出
2、start0()
调用native的本机操作系统函数
由于线程启动需要牵扯操作系统中资源的分配问题,所以具体的线程的启动需要根据不同操作系统有不同的实现,而JVM相当于根据系统中定义的start0()方法来根据不同的操作系统进行该方法的实现,这样在多线程的层次上start0()方法名称不变,而不同的操作系统上有不同的实现。
结论:只有Thread类的start()方法才能够进行操作系统的资源分配,所以启动多线程的方式永远就是调用thread类的start来实现。
三、实现Runnable接口
设计过程中尽量少继承普通类,出现单继承。尽量实现接口或者抽象类。
继承Thread类会产生单继承的局限操作,所以现在最好做法利用接口实现。
使用Runnable接口实现。Runnable接口的结构:
@FunctionalInterface
public interface Runnable {
public abstract void run();//接口定义的抽象方法
}
此时的代码使用的是函数式接口,可以利用lambda表达式完成
1、常规实现
class MyThread2 implements Runnable {
private String name;
public MyThread2(String name) {
this.name = name;
}
@Override
public void run() {// 主方法
for (int i = 0; i < 10; i++) {
System.out.println(this.name + " i:" + i);
}
}
}
如果启动多线程只能依靠Thread类中的start方法,之前继承thread可以使用,现在继承Runnable接口没有此方法。
查看Thread类中的构造方法:public Thread(Runnable target)
MyThread2 mt21 = new MyThread2("线程A");
MyThread2 mt22 = new MyThread2("线程B");
MyThread2 mt23 = new MyThread2("线程C");
new Thread(mt21).start();
new Thread(mt22).start();
new Thread(mt23).start();
2、匿名内部类实现
String name = "线程对象";//以前必须加上finall,由于lamdba,可以不适用
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + " i:" + i);
}
}
}).start();
3、lamda实现【jdk1.8】
由于Runnable接口是函数式接口
String name = "线程对象";//以前必须加上finall,由于lamdba,可以不适用
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(name + " i:" + i);
}
}).start();
结论:只要给出的是函数式接口基本上就可以使用lamda表达式或者是方法引用
四、实现Callable接口【jdk1.5之后】
Runnable执行完毕,不能回调
从JDK1.5之后,对于多线程的实现多了一个Callable接口,在这个接口里面比Runnable接口唯一的强大之处,可以返回执行结果。此接口定义在package java.util.concurrent;包中。
定义:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
这个泛型表示的是返回值类型,call()方法就相当于run()方法。
使用:
class MyThread5 implements Callable<String> {
private int taket=5;
@Override
public String call() {// 主方法
for (int i = 0; i < 10; i++) {
if(taket>0)
System.out.println("卖票 i:" + taket);
}
return "票卖完了!";
}
}
问题:现在Thread类中并没有提供接口Callable接口对象的操作。现在如何启动多线程??
为了分析启动的操作,需要分析继承结构
首先来观察java.util.concurrent.FutureTask<V>结构
方法调用
Callable<String> callable = new MyThread5();
//取得执行结果
FutureTask<String> task = new FutureTask<>(callable);
Thread thread = new Thread(task);
thread.start();
System.out.println(task.get());
对于此种方式没有特殊要求。一般使用Runnable方式
总结:
1.Thread有单继承局限。但是所有线程独享一定要通过Thread中的start启动。
2.Runnable可以避免单继承局限,建议使用
3.Callable比run唯一好处多了返回值的数据。但是使用复杂一些。
五、对于继承Thread方式和实现Runnable接口对比
1、区别和联系
1.多线程需要一个线程的主类,这个类要么继承Thread类,要么实现Runnable接口
2.使用Runnable接口可以比Thread类更好的实现数据共享的操作,并且利用Runnable接口可以避免单继承局限问题
2、分析
两种模式本质上来讲,一定使用Runnable接口实现,这样可以避免单继承局限,除了使用原则之外,还需要知道两种实现方式的联系:
1.Thread类的定义结构:
public class Thread implements Runnable
2.代码分析
class MyThread2 implements Runnable {
private String name;
public MyThread2(String name) {
this.name = name;
}
@Override
public void run() {// 主方法
for (int i = 0; i < 10; i++) {
System.out.println(this.name + " i:" + i);
}
}
}
main方法
MyThread2 mt21 = new MyThread2("线程A");
MyThread2 mt22 = new MyThread2("线程B");
MyThread2 mt23 = new MyThread2("线程C");
new Thread(mt21).start();
new Thread(mt22).start();
new Thread(mt23).start();
分析代码结构,整个代码的操作中使用的就是一个代理设计模式的结构,但是与传统的代理结构有差别。如果按照传统代理模式,现在要想启动多线程,理论上应该是run()方法,但是实质上调用的是start()方法,名称不符合。之所以这样是因为长期发展的产物,以前设计模式不成熟。2000年以后设计模式快速发展。
除了以上继承关联之外,还有一些区别:Runnable接口实现的多线程要比Thread类实现的多线更方便表示出数据共享的概念。
3.数据共享示例
示例:希望有三个线程卖票。
thread实现
class MyThread3 extends Thread {
private int taket=5;
@Override
public void run() {// 主方法
for (int i = 0; i < 10; i++) {
if(taket>0)
System.out.println("卖票 i:" +taket);
}
}
}
main方法
MyThread3 mt31 = new MyThread3();
MyThread3 mt32 = new MyThread3();
MyThread3 mt33 = new MyThread3();
mt31.start();
mt32.start();
mt33.start();
每个线程各自卖各自的票,数据没有被共享
Runnable实现
class MyThread4 implements Runnable {
private int taket=5;
@Override
public void run() {// 主方法
for (int i = 0; i < 10; i++) {
if(taket>0)
System.out.println("卖票 i:" + taket);
}
}
}
main方法实现
MyThread4 mt = new MyThread4();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
002-线程实现方式【thread、runnable、callale、thread和runnable对比】的更多相关文章
- 【转】java线程系列---Runnable和Thread的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...
- 正确停止线程的方式三 使用Thread类中的内置的中断标记位-----------不熟悉
package charpter10; public class Processor implements Runnable { @Override public void run() { for ( ...
- java 线程Thread 技术--创建线程的方式
在第一节中,对线程的创建我们通过看文档,得知线程的创建有两种方式进行实现,我们进行第一种方式的创建,通过继承Thread 类 ,并且重写它的run 方法,就可以进行线程的创建,所有的程序执行都放在了r ...
- java线程系列---Runnable和Thread的区别 (转载)
转自:http://blog.csdn.net/wwww1988600/article/details/7309070 在java中可有两种方式实现多线程,一种是继承 Thread类,一种是实现Run ...
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
转自:http://www.chinaitlab.com/Java/line/942440.html 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互.一般有三种方法,Thread, ...
- java中的线程问题(三)——继承Thread VS 实现Runnable的区别
从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口,如果一定要说它们有什么区别, ...
- 并发编程---线程---开启方式---进程线程的区别----Thread的其他属性
线程 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合)而线程才是cpu上的执行单位 1.同一个进程内的多个线程共享该进程内的地址资源 2.创建线程的开销远小于创建进程的开销(创建一 ...
- 【多线程】创建线程方式一:继承Thread类
创建线程方式一:继承Thread类 代码示例: /** * @Description 继承Thread类,重写run方法,调用start开启线程 * @Author hzx * @Date 2022- ...
- Android笔记——Handler Runnable与Thread的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...
- Java中Runnable和Thread的区别
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...
随机推荐
- 学习 TList 类的实现[2]
我原来以为 TList 可能是一个链表, 其实只是一个数组而已. 你知道它包含着多大一个数组吗? MaxListSize 个!MaxListSize 是 Delphi 在 Classes 单元定义的一 ...
- 属性编辑器,即PropertyEditor-->Spring IoC
在Spring配置文件里,我们往往通过字面值为Bean各种类型的属性提供设置值:不管是double类型还是int类型,在配置文件中都对应字符串类型的字面值.BeanWrapper填充Bean属性时如何 ...
- css揭秘读书笔记
currentColor属性让hr和段落相同的颜色: div { color: red; } hr { background: currentColor; /* 一定要设置高度*/ height: 0 ...
- 虚拟机 搭建LVS + DR + keepalived 高可用负载均衡
一:环境说明: LVS-DR-Master: 10.3.0.82 LVS-DR-Backup: 10.3.0.70 VIP: 10.3.0.60 ...
- c++ 函数返回研究[转]
一,c++函数的返回分为以下几种情况 1)主函数main的返回值:这里提及一点,返回0表示程序运行成功. 2)返回非引用类型:函数的返回值用于初始化在跳用函数出创建的临时对象.用函数返回值初始化临时对 ...
- activemq 实战 一
This chapter covers Introduction to the use case for each of the book examples Use of Maven for ...
- android 软键盘回车键捕获
EditText editText2 = (EditText)findViewById(R.id.txtTest2); editText2.setOnEditorActionListener(new ...
- tortoiseSVN如何发现和解决冲突?
版本冲突原因: 假设A.B两个用户都在版本号为100的时候,更新了kingtuns.txt这个文件,A用户在修改完成之后提交kingtuns.txt到服务器,这个时候提交成功,这个时候kingtuns ...
- 【转】stm32中断嵌套全攻略
断断续续学习STM32一学期了,时间过的好快,现在对STM32F103系列单片机的中断嵌套及外部中断做一个总结,全当学习笔记.废话不多说,ARM公司的Cortex-m3 内核,支持256个中断,其中包 ...
- Linux系统下JDK安装配置(转载)
转载出处:http://www.cnblogs.com/xuliangxing/p/7066913.html 本文主要介绍的是如何是Linux环境下安装JDK的,因为Linux环境下,很多时候也离不开 ...