Java核心技术之多线程的实现

Java实现多线程的四种方式

1、继承Thread类
2、实现Runnable接口
3、使用Future Task
4、使用Executor框架

继承Thread类和实现Runnable接口是最基本的方式,但有一个共同的缺点:没有返回值。而Future Task解决了这个问题。Executor是JDK提供的一个多线程框架。

Java8创建一个新的执行线程有两种方法:
1、一种是将一个类声明为Thread的子类。这个子类应该重写run 类的方法Thread。然后可以分配并启动子类的实例。
2、另一种创建一个线程是声明实现类Runnable接口。那个类然后实现了run方法。然后可以分配类的实例,在创建Thread时作为参数传递,并启动。
注意:使用继承Thread类创建线程,最大的局限就是不能多继承,所以为了支持多继承,可以采用实现Runnable接口的方式。

继承Thread类实现独立线程单词抄写

模拟一个业务场景:
在这里插入图片描述
先来看第一种实现方式:继承Thread类,重写run方法的方式。
下面通过程序来模拟小明抄写单词的任务。小编觉得抄写次数100有点多,为了减轻小明的工作量,下面代码用的都是抄写10次。
1、定义Punishment.java类,存储要抄写的单词,以及剩余抄写次数。

package com.JavaCoding.ManyThread;

import lombok.Data;

@Data
public class Punishment {

    private int leftCopyCount;
    private String wordToCopy;

    public Punishment(int leftCopyCount, String wordToCopy) {
        this.leftCopyCount = leftCopyCount;  //剩余抄写次数
        this.wordToCopy = wordToCopy;  //要抄写的单词
    }
}

2、定义Student.java类,引用Punishment,实现抄写单词的copyWord方法。

package com.JavaCoding.ManyThread_Thread;

import com.JavaCoding.ManyThread.Punishment;

//1、继承Thread类
public class Student extends Thread {
    private String name;
    private Punishment punishment;

    public Student(String name, Punishment punishment) {
        //2、调用Thread构造方法,设置threadname
        super(name);
        this.name = name;
        this.punishment = punishment;
    }

    public void copyWord(){
        int count=0;
        String threadName=Thread.currentThread().getName();

        while (true){
            if(punishment.getLeftCopyCount()>0){
                int leftCopyCount=punishment.getLeftCopyCount();
                System.out.println(threadName+"线程-"+name+"抄写"+punishment.getWordToCopy()+"。还要抄写"+--leftCopyCount+"次");
                punishment.setLeftCopyCount(leftCopyCount);
                count++;
            }else {
                break;
            }
        }
        System.out.println(threadName+"线程-"+name+"一共抄写了"+count+"次!");
    }

    //3、重写run方法,调用copyWord
    public void run(){
        copyWord();
    }
}

其中Student构造函数传入Punishment,copyWord方法是根据惩罚内容,完成单词抄写的主要逻辑。
copyWord方法中,count变量是计数器,记录抄写的总次数。threadName是本线程的名称,这里通过Thread的静态方法currentThread取得当前线程,然后通过getName方法获取线程名称。
While循环中,当punishment的剩余抄写次数大于0时,执行抄写逻辑,否则抄写任务完成,跳出循环。
3、通过main方法运行,查看运行效果。

package com.JavaCoding.ManyThread_Thread;

import com.JavaCoding.ManyThread.Punishment;

public class StudentClient {

    public static void main(String[] args) {
        Punishment punishment=new Punishment(10,"internationalization");
        Student student=new Student("小明",punishment);
        student.start();
        System.out.println("Another thread will finish the punishment.main thread is finished");
    }
}

start方法是从Thread类继承而来,调用后线程进入就绪状态,等待CPU的调用。而start方法最终会触发执行run方法,在run方法中copyWord被执行。

运行之后的结果显示为:
在这里插入图片描述

实现Runnable接口,启动单线程抄写单词

同样的例子,来看一下用实现Runnable接口来实现多线程。
1、定义Student类, 实现Runnable接口

package com.JavaCoding.ManyThread_Runnable;

import com.JavaCoding.ManyThread.Punishment;
import org.junit.runner.RunWith;

public class Student implements Runnable {
    private  String  name;
    private Punishment punishment;

    public Student(String name, Punishment punishment) {
        this.name = name;
        this.punishment = punishment;
    }

    public  void  copyWord(){
        int count=0;
        String threadName=Thread.currentThread().getName();

        while (true){
            if(punishment.getLeftCopyCount()>0){
                int leftCopyCount=punishment.getLeftCopyCount();
                System.out.println(threadName+"线程-"+name+"抄写"+punishment.getWordToCopy()+"。还要抄写"+--leftCopyCount+"次");
                punishment.setLeftCopyCount(leftCopyCount);
                count++;
            }else {
                break;
            }
        }
        System.out.println(threadName+"线程-"+name+"一共抄写了"+count+"次");
    }
    
    //重写run方法,完成任务
    @Override
    public void run(){
        copyWord();
    }
}

2、定义一个StudentClient类,main方法运行,查看显示的效果

package com.JavaCoding.ManyThread_Runnable;

import com.JavaCoding.ManyThread.Punishment;

public class StudentClient {

    public static void main(String[] args) {
        Punishment punishment=new Punishment(10,"internationalization");
        Thread xiaoming=new Thread(new Student("小明",punishment),"小明");
        xiaoming.start();
    }
}

main方法中需要创建一个Thread,把实现了Runnable接口的对象通过构造函数传递进去,Thread构造函数的第二个参数是自定义的thread name。继承Thread类的实现方式中,由于Student是Thread的子类,所以可以直接通过new Student 就可以得到线程对象。最后都是通过调用Thread对象的start方法来启动线程。运行结果和继承Thread的方式结果一样。
在这里插入图片描述

继承Thread类和实现Runnable接口 实现多线程的区别

1、继承Thread方式

Student xiaoming=new Student("小明",punishment);
xiaoming.start();

2、实现Runnable方式

Thread xiaoming=new Thread(new Student("小明",punishment),"小明");
xiaoming.start();

第一种方式中,Student继承了Thread类,启动时调用了start方法,其实用的是父类Thread的start方法,并最终触发执行Student重写的run方法。

第二种方式中,Student实现Runnable接口,作为参数传递给Thread构造函数。接下来还是调用了Thread的start方法。最后则会触发传入的Runnable实现的run方法。

两种方式都是创建Thread或者Thread的子类,通过Thread的start方法启动。唯一不同的是,第一种run方法实现在Thread子类中。第二种则是把run方法逻辑转移到Runable的实现类中。线程启动后,第一种方式是thread对象运行自己的run方法逻辑,第二种方式则是调用Runnable 实现的run方法逻辑。

实现Runnable接口比继承Thread类实现多线程更好的原因:
1、java语言中只能单继承,通过实现接口的方式,可以让实现类去继承其他类。而直接继承Thread就不能再继承其他类了。
2、线程控制逻辑在Thread类中,业务运行逻辑在Runnable实现类中。解耦更为彻底
3、实现Runnable的实例,可以被多个线程共享并执行。而实现thread是做不到这一点的。

小结

通过上文的描述,我们可以看出,Java中的多线程的实现采用了模板模式。Thread是模板对象,负责线程相关的逻辑,比如线程的创建,运行以及各种操作。而线程真正的业务则被剥离出来,交由Runnable的实现类去实现。线程操作和业务逻辑完全解耦,开发者只需要聚焦在业务逻辑上的实现即可。

原创文章 209 获赞 264 访问量 11万+

猜你喜欢

转载自blog.csdn.net/Sophia_0331/article/details/105900768