专栏首页小怪聊职场Java中实现多线程的3种方法介绍和比较

Java中实现多线程的3种方法介绍和比较

一、什么是多线程?

引用网上别人的一段话:

  1. 单进程单线程:一个人在一个桌子上吃菜。
  2. 单进程多线程:多个人在同一个桌子上一起吃菜。
  3. 多进程单线程:多个人每个人在自己的桌子上吃菜。

多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。资源共享就会发生冲突争抢。

使用多线程的优点(相对使用多进程来说):

  1. 进程之间不能共享内存,但线程之间共享内存非常容易。
  2. 系统创建线程所分配的资源相对创建进程而言,代价非常小。

二、Java中实现多线程的3种方法介绍和比较

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

这三种方法的介绍和比较 1、实现Runnable接口相比继承Thread类有如下优势 1)可以避免由于Java的单继承特性而带来的局限 2)增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的 3)适合多个相同程序代码的线程去处理同一资源的情况 4)线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类

2、实现Runnable接口和实现Callable接口的区别 1)Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的 2)实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回结果 3)Callable接口的call()方法允许抛出异常,而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛 4)加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法 注:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞

三、Runnable、Thread、Callable案例

3.1、第一种实现方法—继承Thread类

继承Thread类,需要覆盖方法 run()方法,在创建Thread类的子类时需要重写 run(),加入线程所要执行的代即可。

package cn.huangt.java_learn_notes.multithread;

/**
 * 继承Thread实现多线程
 * @author huangtao
 */
public class ThreadExtends {
    public static void main(String[] args) {
        new MyThread("Thread测试").start();
        new MyThread("Thread测试").start();
    }
}

class MyThread extends Thread{
    
    private String acceptStr;
    
    public MyThread(String acceptStr) {
        this.acceptStr = acceptStr;
    }
    
    public void run() {
        
        for (int i = 0; i < 5; i ++) {
            System.out.println("这个传给我的值:"+acceptStr+",加上一个变量,看看是什么效果:"+i);
        }
    }
}

/*
输出内容===
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:0
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:0
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:1
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:2
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:3
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:4
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:1
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:2
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:3
这个传给我的值:Thread测试,加上一个变量,看看是什么效果:4
*/

3.2、第二种实现方法—实现Runnable接口

如果要实现多继承就得要用implements,Java 提供了接口 java.lang.Runnable 来解决上边的问题。

Runnable是可以共享数据的,多个Thread可以同时加载一个Runnable,当各自Thread获得CPU时间片的时候开始运行Runnable,Runnable里面的资源是被共享的,所以使用Runnable更加的灵活。PS:需要解决共享之后产生的资源竞争问题。

package cn.huangt.java_learn_notes.multithread;

/**
 * Runnable接口实现多线程
 * @author huangtao
 */
public class RunnableImpl implements Runnable {

    private String acceptStr;
    
    public RunnableImpl(String acceptStr) {
        this.acceptStr = acceptStr;
    }

    public void run() {
        try {
            // 线程阻塞1秒,此时有异常产生,只能在方法内部消化,无法上抛
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 最终处理结果无法返回
        System.out.println("hello : " + this.acceptStr);
    }

    public static void main(String[] args) {
        Runnable runnable = new RunnableImpl("Runable测试");
        long beginTime = System.currentTimeMillis();
        new Thread(runnable).start();
        long endTime = System.currentTimeMillis();
        
        // endTime 和 beginTime是一样的,线程并不会阻塞主线程
        System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
    }
}

/*
输出内容===
cast : 0 second!
hello : Runable测试
*/

3.3、第三种—实现Callable接口

Runnable是执行工作的独立任务,但是它不返回任何值。如果你希望任务在完成的能返回一个值,那么可以实现Callable接口而不是Runnable接口。在Java SE5中引入的Callable是一种具有类型参数的泛型,它的参数类型表示的是从方法call()(不是run())中返回的值。

package cn.huangt.java_learn_notes.multithread;

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

/**
 * Callable接口实现多线程
 * @author huangtao
 */
public class CallableImpl implements Callable<String> {
    
    private String acceptStr;
    
    public CallableImpl(String acceptStr) {
        this.acceptStr = acceptStr;
    }

    public String call() throws Exception {
        // 任务阻塞1秒,并且增加一些信息返回
        Thread.sleep(1000);
        return this.acceptStr + " 增加一些字符并返回";
    }
    
    public static void main(String[] args) throws Exception {
        Callable<String> callable = new CallableImpl("Callable测试");
        FutureTask<String> task = new FutureTask<String>(callable);
        // 创建线程
        new Thread(task).start();
        long beginTime = System.currentTimeMillis();
        // 调用get()阻塞主线程,反之,线程不会阻塞
        String result = task.get();
        long endTime = System.currentTimeMillis();
        System.out.println("hello : " + result);
        
        // endTime 和 beginTime是不一样的,因为阻塞了主线程
        System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");
    }
}

/*
输出内容===
hello : Callable测试 增加一些字符并返回
cast : 1 second!
*/

四、Runnable、Thread、Callable总结

最后再来看看它们三个之间的总结。

4.1、实现Runnable接口相比继承Thread类有如下优势

1)可以避免由于Java的单继承特性而带来的局限 2)增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的 3)适合多个相同程序代码的线程去处理同一资源的情况 4)线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类

4.2、实现Runnable接口和实现Callable接口的区别

1)Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的 2)实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回结果 3)Callable接口的call()方法允许抛出异常,而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛 4)加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法 注:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞

五、其他

当然,关于多线程,只掌握这些肯定不够。 还有多线程的实现原理,还有深入理解Java线程池,这样才能更好地使用多线程。 我在后面的文章中会更新。 文章中的代码在我的GitHub上:https://github.com/huangtao1208/java_learn_notes

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 爬虫课程(五)|十分钟学会使用正则表达式

    黄小怪
  • Jenkins+Git+Maven+Tomcat8配置web自动构建环境(拉代码、编译、打包、部署)

    黄小怪
  • MySQL(二)|深入理解MySQL的四种隔离级别及加锁实现原理

    黄小怪
  • 看完本文,再也不怕问java线程创建的几种方式了

    使用继承Thread类的方法来创建线程类时候,多个线程之间是无法共享线程类的实例变量的。

    田维常
  • 线程

    使用ExecutorService、Callable、Future实现有返回结果的多线程。

    大学里的混子
  • java多线程系列_使用Runnable接口创建线程(3)

    实现Runnable接口的类必须使用Thread类的实例才能创建线程。通过Runnable接口创建线程分为两步:

    Hongten
  • java中Runnable和Callable的区别

    在java的多线程开发中Runnable一直以来都是多线程的核心,而Callable是java1.5添加进来的一个增强版本。

    程序那些事
  • Java 后台开发面试题分享七

    3) Char 类型,如果存入数据的实际长度比指定长度要小,会补空格至指定长度;如果存入的数据的实际长度大于指定长度,低版本会被截取,高版本会报错

    RendaZhang
  • [Java]Java实现线程操作的两种方法

    原创文章,欢迎转载。转载请注明:转载自 祥的博客 原文链接:http://blog.csdn.net/humanking7/article/details/...

    祥知道
  • 实战:爬取简书之多线程爬取(一)

    在上上篇我们编写了一个简单的程序框架来爬取简书的文章信息,10分钟左右爬取了 1万 5千条数据。

    渔父歌

扫码关注云+社区

领取腾讯云代金券