一、概述

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对比】的更多相关文章

  1. 【转】java线程系列---Runnable和Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  2. 正确停止线程的方式三 使用Thread类中的内置的中断标记位-----------不熟悉

    package charpter10; public class Processor implements Runnable { @Override public void run() { for ( ...

  3. java 线程Thread 技术--创建线程的方式

    在第一节中,对线程的创建我们通过看文档,得知线程的创建有两种方式进行实现,我们进行第一种方式的创建,通过继承Thread 类 ,并且重写它的run 方法,就可以进行线程的创建,所有的程序执行都放在了r ...

  4. java线程系列---Runnable和Thread的区别 (转载)

    转自:http://blog.csdn.net/wwww1988600/article/details/7309070 在java中可有两种方式实现多线程,一种是继承 Thread类,一种是实现Run ...

  5. java 创建线程的三种方法Callable,Runnable,Thread比较及用法

    转自:http://www.chinaitlab.com/Java/line/942440.html 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互.一般有三种方法,Thread, ...

  6. java中的线程问题(三)——继承Thread VS 实现Runnable的区别

    从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口,如果一定要说它们有什么区别, ...

  7. 并发编程---线程---开启方式---进程线程的区别----Thread的其他属性

    线程 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合)而线程才是cpu上的执行单位 1.同一个进程内的多个线程共享该进程内的地址资源 2.创建线程的开销远小于创建进程的开销(创建一 ...

  8. 【多线程】创建线程方式一:继承Thread类

    创建线程方式一:继承Thread类 代码示例: /** * @Description 继承Thread类,重写run方法,调用start开启线程 * @Author hzx * @Date 2022- ...

  9. Android笔记——Handler Runnable与Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  10. Java中Runnable和Thread的区别

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

随机推荐

  1. 学习 TList 类的实现[2]

    我原来以为 TList 可能是一个链表, 其实只是一个数组而已. 你知道它包含着多大一个数组吗? MaxListSize 个!MaxListSize 是 Delphi 在 Classes 单元定义的一 ...

  2. 属性编辑器,即PropertyEditor-->Spring IoC

    在Spring配置文件里,我们往往通过字面值为Bean各种类型的属性提供设置值:不管是double类型还是int类型,在配置文件中都对应字符串类型的字面值.BeanWrapper填充Bean属性时如何 ...

  3. css揭秘读书笔记

    currentColor属性让hr和段落相同的颜色: div { color: red; } hr { background: currentColor; /* 一定要设置高度*/ height: 0 ...

  4. 虚拟机 搭建LVS + DR + keepalived 高可用负载均衡

    一:环境说明:   LVS-DR-Master:    10.3.0.82   LVS-DR-Backup:    10.3.0.70   VIP:                10.3.0.60  ...

  5. c++ 函数返回研究[转]

    一,c++函数的返回分为以下几种情况 1)主函数main的返回值:这里提及一点,返回0表示程序运行成功. 2)返回非引用类型:函数的返回值用于初始化在跳用函数出创建的临时对象.用函数返回值初始化临时对 ...

  6. activemq 实战 一

    This chapter covers  Introduction to the use case for each of the book examples  Use of Maven for ...

  7. android 软键盘回车键捕获

    EditText editText2 = (EditText)findViewById(R.id.txtTest2); editText2.setOnEditorActionListener(new ...

  8. tortoiseSVN如何发现和解决冲突?

    版本冲突原因: 假设A.B两个用户都在版本号为100的时候,更新了kingtuns.txt这个文件,A用户在修改完成之后提交kingtuns.txt到服务器,这个时候提交成功,这个时候kingtuns ...

  9. 【转】stm32中断嵌套全攻略

    断断续续学习STM32一学期了,时间过的好快,现在对STM32F103系列单片机的中断嵌套及外部中断做一个总结,全当学习笔记.废话不多说,ARM公司的Cortex-m3 内核,支持256个中断,其中包 ...

  10. Linux系统下JDK安装配置(转载)

    转载出处:http://www.cnblogs.com/xuliangxing/p/7066913.html 本文主要介绍的是如何是Linux环境下安装JDK的,因为Linux环境下,很多时候也离不开 ...