【多线程高并发编程】二 实现多线程的几种方式

限于喜欢 提交于 2020-05-07 13:46:42

程序猿学社的GitHub,欢迎Star
https://github.com/ITfqyd/cxyxs
本文已记录到github,形成对应专题

前言:

上章,我们已经了解线程的一些基本概念。本文我们来看看多线程的应用场景,为什么要用多线程,以及实现一个多线程有几种方式。

1.什么是多线程?

多线程是指通过软件优化和硬件(CPU)的方式,同时并发运行多个线程(任务)。更好的运行系统的资源。

例如,社长,很久以前,接到boss的提的一个业务,需要开发一个充电桩管理物联网管理平台,实现通过网站,查看各个充电桩的情况。如果就社长一个人开发,感觉1年搞定都有点难,毕竟社长专注于后端开发,这时社长就跟boss提出,需要增加人马,招一个前端,一个后端。社长就负责跟硬件对接,每个人负责一块,各种同步开发。最后,通过社长三人的努力,半年就交差了。这就是多线程的好处。多个人,信息也是共享(一个进程内的多个线程,资源是共享在同一个内存中)
在这里插入图片描述

1.1应用场景

  • 网站发送多个请求,会一一返回结果,也就是很高的使用了多线程。如果没有多线程,我们抢票,就得发一个请求后,需要等请求处理完后,才能运行。
  • 扣扣聊天界面,如果没有多线程,发一个消息,需要上一个消息处理完后,才能处理下一个需求。
  • 通过netty解析数据报文,如果没有多线程,1w个线程,直接怼进来,我们只能一个个处理,肯定处理不过来,如果没有多线程,解析逻辑也无法和业务逻辑分离开,实现程序的解耦。

2.实现一个多线程的常见几种方式

为了模拟真实的场景,每个线程中,都增加了延迟运行的代码。

Thread.sleep(1000);

这句代码表示休眠1秒钟,以毫秒为单位。

通过继承的方式,实现多线程(第一种)

package com.cxyxs.two;

import java.util.Date;

/**
 * Description:第一种:通过继承的方式,实现多线程
 *  转发请注明来源  程序猿学社 - https://ithub.blog.csdn.net/
 * Author: 程序猿学社
 * Date:  2020/2/17 21:37
 * Modified By:
 */
public class MyThreadExtend extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("程序猿学社:社长在开发中,通过继承的方式实现:" + new Date());
        }
    }
}

调用代码

  //第一种方式
  MyThreadExtend threadExtend = new MyThreadExtend();
  threadExtend.start();
  • 因为java中是单继承,所以不推荐通过这种方式实现多线程。如果该类已经被继承,是无法继承Thread类的。

通过实现runnable接口,实现多线程(第二种)

package com.cxyxs.two;

import java.util.Date;

/**
 * Description:转发请注明来源  程序猿学社 - https://ithub.blog.csdn.net/
 * Author: 程序猿学社
 * Date:  2020/2/17 21:43
 * Modified By:
 */
public class MyThreadRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("小二在开发中,通过实现Runnable接口方式实现:" + new Date());
        }
    }
}

调用代码

//第二种方式
MyThreadRunnable runnable = new MyThreadRunnable();
Thread thread = new Thread(runnable);
thread.start();

匿名内部类实现(第三种)

Runnable方式

//匿名内部类-第一种
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("通过匿名内部类的方式第一种实现!");
            }
        }).start();

继承类方式

//匿名内部类-第二种
        new Thread(){
            @Override
            public void run() {
                System.out.println("通过匿名内部类的方式第二种实现!");
            }
        }.start();

通过Callable实现多线程(第四种)

public class MyThreadCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sum+=i;
            System.out.println("小王通过实现Callable接口的方式实现:" + new Date());
        }
        return sum;

    }
}

调用代码

//第四种 通过Callable实现多线程
        MyThreadCallable callable = new MyThreadCallable();
        FutureTask<Integer> result = new FutureTask<Integer>(callable);
        new Thread(result).start();
        try {
            Integer sum =  result.get();
            System.out.println("计算结果:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
  • 之前几种实现多线程的方法,需要重写run方法,启动多线程。而Callable方式实现多线程,需要重写call方法。通过FutureTask包装器来创建Thread线程
  • 通过调用get方法获取多线程的运行结果。注意,get方法会一直堵塞,没有返回值,主线程会一直等待。
  • 应用场景,例如批量大数据的导出,假设我们要导出100w数据,需要30s,我们就可以通过分页,每个线程查10w的数据,启动10个线程,来获取处理结果。这样就可以通过多线程提供查询的效率。

启动一个main线程,启动了几个线程

 public class ThreadCount {
    public static void main(String[] args) {
        ThreadGroup group =
                Thread.currentThread().getThreadGroup();
        int count = group.activeCount();
        group.list();
    }
}

idea打印
在这里插入图片描述
eclipse打印
在这里插入图片描述
启动了2个线程,一个主线程main,是程序的入口。
还有一个[Monitor Ctrl-Break,这是IDEA特有的监控线程。正确的打印应该是gc线程。通过上面两张图,就可以得出这个结论。




多个线程进行测试

public class Test {
    public static void main(String[] args) {
        //第一种方式
        MyThreadExtend threadExtend = new MyThreadExtend();
        threadExtend.start();

        //第二种方式
        MyThreadRunnable runnable = new MyThreadRunnable();
        Thread thread = new Thread(runnable);
        thread.start();

        //第三种方式
        //匿名内部类-第一种
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("通过匿名内部类的方式第一种实现!");
            }
        }).start();

        //匿名内部类-第二种
        new Thread() {
            @Override
            public void run() {
                System.out.println("通过匿名内部类的方式第二种实现!");
            }
        }.start();

        //第四种 通过Callable实现多线程
        MyThreadCallable callable = new MyThreadCallable();
        FutureTask<Integer> result = new FutureTask<Integer>(callable);
        new Thread(result).start();
        try {
            Integer sum =  result.get();
            System.out.println("计算结果:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  • 通过测试的结果,我们发现,数据是交换运行的,如果是单cpu,每次只能运行一个通道,就算用多线程的方式实现,每次也是交替运行。只是计算机运行速度很快,我们看不出什么区别。说到这里,就有社友提出疑问,单cpu,还有必要用多线程吗
    就算是单cpu,利用多线程也有很多好处。让我们可以减少没必要的等待,更好的利用资源。
  • 如果是多cpu,就可以同时并发运行多个任务,大大的提高运行效率。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!