程序猿学社的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,就可以同时并发运行多个任务,大大的提高运行效率。
来源:oschina
链接:https://my.oschina.net/u/4271740/blog/4269211