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();

时间: 2024-10-05 09:17:19

002-多线程实现方式【thread、runnable、callale、thread和runnable对比】的相关文章

二、java实现多线程的方式?

一.同步?异步? 下面两幅图解释了同步异步. 二.实现多线程的方式 1.继承Thread package threaddemo; class CreateThreadDemo extends Thread{ /** * 线程执行的任务,执行的代码 */ @Override public void run() { for (int i = 0; i <30 ; i++) { System.out.println("我是线程:"+i+ "名字:"+Thread.c

JAVA学习第二十二课(多线程(二))- (多线程的创建方式一 :继承Thread类)

线程是程序中的执行线程.Java 虚拟机允许应用程序并发地运行多个执行线程. 创建新执行线程有两种方法. 一种方法是将类声明为 Thread 的子类.该子类应重写Thread 类的run 方法.另一种方法是声明实现 Runnable 接口的类.该类然后实现run 方法. 创建线程方式一:继承Thread类 一.创建线程的步骤: 1.定义一个类继承Thread类 2.覆盖Thread中的run()方法 3.直接创建Thread类子类的对象 4.调用start方法开启线程,并调用线程的任务run方法

Java多线程中的Runnable和Thread

摘要: 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限. 用法: Thread: package org.thread.demo;class MyThread extends Thread{ private String name; public MyThread(Stri

【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17161237 Java中实现多线程有两种方法:继承Thread类.实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势: 1.可以避免由于Java的单继承特性而带来的局限: 2.增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的: 3.适合多个相同程序代码的线程区处理同一资源的情

Runnable和Thread实现多线程的区别(含代码)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17161237 Java中实现多线程有两种方法:继承Thread类.实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势: 1.可以避免由于Java的单继承特性而带来的局限: 2.增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的: 3.适合多个相同程序代码的线程区处理同一资源的情

Java 多线程之 Runnable VS Thread 以及资源共享问题

对于 Java 多线程编程中的 implements Runnable 与 extends Thread,部分同学可能会比较疑惑,它们之间究竟有啥区别和联系呢?他们是不是没啥区别随便选呢?实际中究竟该选择哪一个呢? 甚至网上不少博客文章以讹传讹得出不少谬论,那今天的走进科学栏目将带您一一揭开谜底. 1.区别: 其实这块主要是围绕着接口和抽象类的区别以及一些设计原则而言的. 1.1 Inheritance Option: The limitation with "extends Thread&qu

java:多线程基础之Runnable与Thread

java.lang包下有二个非常有用的东西:Runnable接口与Thread类,Thread实现了Runnable接口(可以认为Thread是Runnable的子类),利用它们可以实现最基本的多线程开发. 一.Runnable入门示例 1 public class RunnableDemo1 { 2 3 public static void main(String[] args) { 4 new Runnable() { 5 public void run() { 6 for (int i =

Java并发编程(6):Runnable和Thread实现多线程的区别(含代码)

Java中实现多线程有两种方法:继承Thread类.实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势: 1.可以避免由于Java的单继承特性而带来的局限: 2.增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的: 3.适合多个相同程序代码的线程区处理同一资源的情况. 下面以典型的买票程序(基本都是以这个为例子)为例,来说明二者的区别. 首先通过继承Thread类实现,代码如下: cl

Android开发:Handler Runnable和Thread之间的区别和联系 应用--------------------看完本篇,从此一览无余!

http://blog.csdn.net/yanzi1225627/article/details/8582081 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限. 下面看例子: package org.thread.demo; class MyThread extends

多线程_创建线程_继承Thread类

public class ThreadDemo {   public static void main(String[] args){         Demo d = new Demo();   d.start();      for(int i = 0;i < 100;i++){      System.out.println("MainThread" + i);   }   } } class Demo extends Thread {   public void run(