JAVA——多线程的实现方式


前言


Java目前对于多线程具有三种实现的方式:1.继承Thread 2.实现Runnable接口 3.实现Callable接口,本文章将对这三种方法进行分析

一、继承Thread

1.使用

        通过创建一个类去继承Thread,代表了这个类成为了为一个线程的主体类,这个类就可以进行线程的创建了,但是还要覆写父类的run()方法才能执行任务,run()方法代表线程要处理的任务(线程的主方法——类似主程序的main方法)

范例:

public class MyThread extends Thread {
    @Override
    public void run() {
    	//设置子线程的名字 测试的时候更直观
        setName("我的线程");
        for(int x = 0; x< 10 ; x++){
            System.out.println(Thread.currentThread().getName()+"------>"+x);
        }
    }
}

2.测试

        在主线程中通过创建MyThread实例化对象调用start()方法创建线程

public class{
    public static void main(String[] args) {
        //设置主线程名字 更直观观看
        Thread.currentThread().setName("主线程");
        new MyThread().start();
        for(int x = 0;x < 10; x++ ){
            System.out.println(Thread.currentThread().getName()+"------>"+x);
        }
    }
}

测试结果:
在这里插入图片描述
        从测试的结果可以看出,主线程和子线程是并发执行的,执行的顺序具有随机性。
        原因:因为java多线程的实现是抢占时调度——多个线程对CPU的争夺,谁抢到了CPU的使用权,那么抢到的线程才可以运行。ps:优先让优先级高的线程进行运行,如果优先级一样就大家一起抢。
        总结:java多线程使用的本质是CPU以人无法察觉的速度对线程进行快速的切换运行,产生了多线程的并发效果

二、实现Runnable接口

1.使用

        这种方式的实现也很简单,就是把继承Thread类改为实现Runnable接口。代码如下:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int x = 0; x< 10 ; x++){
            System.out.println(Thread.currentThread().getName()+"------>"+x);
        }
    }
}

2.测试

代码如下:
将Runnable对象传入Thread类中使用(将任务传入线程),再启动线程

public class ThreadTest{
    public static void main(String[] args) {
        //设置主线程名字 更直观观看
        Thread.currentThread().setName("主线程");
        //
        new Thread(new MyRunnable(),"子线程").start();
        for(int x = 0;x < 10; x++ ){
            System.out.println(Thread.currentThread().getName()+"------>"+x);
        }
    }
}

测试结果:
在这里插入图片描述
        从测试的结果可以看出,这种方式和第一种方式执行结果是一样的,那么为什么会有第二种方式出现呢?

原因:因为由于继承机制的有局限性,继承只能继承一个父类,无法扩展功能,所以才会产生Runnable接口,特别用来覆写方法run()给Thread类提供任务,而Thread类专门用来产生线程,分工合作。这种方式也更合理,实际上我们应用这种方式也多于第一种方式。

额外知识点: 可以发现从JDK1.8开始,Runnable接口使用了函数式接口定义,所以也可以直接利用Lambda表达式进行线程类的实现定义

范例如下:

public class LambdaTest{
    public static void main(String[] args) {
        //设置主线程名字 更直观观看
        Thread.currentThread().setName("主线程");
        //lambda方法格式 () -> {run方法体} 大括号可用 可不用 匿名内部类的简易版
        new Thread(() -> {
            for(int x = 0; x< 10 ; x++){
                System.out.println(Thread.currentThread().getName()+"------>"+x);
            }
        },"Lambda传入子线程").start();
        for(int x = 0;x < 10; x++ ){
            System.out.println(Thread.currentThread().getName()+"------>"+x);
        }
    }
}

3.继承Thread和实现Runnable的对比

1.第一种方法十分简便,直接在继承Thread的类中重写run()方法并直接启动即可,但是具有单继承的局限性缺点
2.而第二种方法复杂了一点,通过实现接口Runnable来实现对线程任务的实现,但是可以多实现,使得开发更加灵活

三、实现Callable接口

        由于Runnable接口和Thread都有一个缺点:当线程执行完毕后,我们无法获取一个返回值,并且run()方法也无法抛出异常,所以从JDK1.5之后就提出了一个新的线程实现接口:java.util.concurrent.Callable接口。

1.使用

因为Thread类中没有实现Callable接口,所以实现Callable的类不能像实现Runnable的类一样直接传入参数给Thread类中,但是FutureTask这个类中实现了Runnable并且也可以接收Callable的call方法——类似Runnable的run方法,所以
代码如下:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadTest{
    public static void main(String[] args) {
        //设置主线程名字 更直观观看
        Thread.currentThread().setName("主线程");
        //利用lambda方式建立Callable的子类对象
        Callable<String> call = () -> {
            for(int x = 0; x< 10 ; x++){
                System.out.println(Thread.currentThread().getName()+"------>"+x);
            }
            return "该线程执行成功";
        };
        //创建FutureTask对象 并传入Callable子对象
        FutureTask<String> ft = new FutureTask<>(call);
        //创建子线程
        new Thread(ft,"Callable传入子线程").start();
        //主线程执行
        for(int x = 0;x < 10; x++ ){
            System.out.println(Thread.currentThread().getName()+"------>"+x);
        }
        //获取子线程返回的值
        try {
            String s = ft.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

2.测试

在这里插入图片描述
该线程方式可以获取得到返回值,该图片最后一句“该线程执行成功”就是我得到的返回值

面试题: Runnable 与 Callable的区别?

  1. Runnable没有返回值;Callable可以返回执行结果
  2. Callable接口的call()允许抛出异常;Runnable的run()不能抛出

总结

1、任何一个线程的对象都使用Thread类进行封装,线程的启动使用的是都是start(),启动的时候线程进入的是一种就绪状态,并没有执行

2、进入到就绪状态之后就需要等待进行资源调度,当某一个线程调度成功之后则进入到运行状态(run()方法),但是所有的线程不可能一直持续执行下去,中间需要产生一些暂停状态,例如:阻塞状态,因为线程不可一直占有资源,所以当线程让出资源的时候,该线程就会进入阻塞状态,然后等待下一次调度资源,进行运行

3、当run()方法执行完毕之后,实际上该线程的主要任务也就结束了,那么此时就可以直接进入到停止状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值