前言
很多敌人面对多线程的问题都很头大,因为本人做我的项目很难用到,然而凡是高薪的职位面试都会问到。。毕竟当初大厂里用的都是多线程高并发,所以这块内容不吃透必定是不行的。
明天这篇文章,作为多线程的根底篇,先来谈谈以下问题:
- 为什么要用多线程?
- 程序 vs 过程 vs 线程
- 创立线程的 4 种形式?
我这里筹备了一线大厂面试材料和我原创的超硬核 PDF 技术文档,以及我为大家精心筹备的多套简历模板(不断更新中),心愿大家都能找到心仪的工作!须要的敌人点击这里备注思否自行下载即可,心愿对你们有帮忙!
为什么要用多线程
任何一项技术的呈现都是为了解决现有问题。
之前的互联网大多是单机服务,体量小;而当初的更多是集群服务,同一时刻有多个用户同时拜访服务器,那么会有很多线程并发拜访。
比方在电商零碎里,同一时刻比方整点抢购时,大量用户同时拜访服务器,所以当初公司里开发的根本都是多线程的。
应用多线程的确进步了运行的效率,但与此同时,咱们也须要特地留神数据的增删改状况,这就是线程平安问题,比方之前说过的 HashMap vs HashTable
,Vector vs ArrayList
。
要保障线程平安也有很多形式,比如说加锁,但又可能会呈现其余问题比方死锁,所以多线程相干问题会比拟麻烦。
因而,咱们须要了解多线程的原理和它可能会产生的问题以及如何解决问题,能力拿下高薪职位。
过程 vs 线程
程序 program
说到过程,就不得不先说说程序。
程序,说白了就是代码,或者说是一系列指令的汇合。比方「微信.exe」这就是一个程序,这个文件最终是要拿到 CPU 外面去执行的。
过程 process
当程序运行起来,它就是一个 过程。
所以 程序是“死”的,过程是“活”的。
比方在工作管理器里的就是一个个过程,就是“动起来”的应用程序。
Q:这些过程是并行执行的吗?
单核 CPU 一个工夫片里只能执行一个过程。然而因为它切换速度很快,所以咱们感触不到,就造成了一种多过程的假象。(多核 CPU 那真的就是并行执行的了。)
Q:那如果这个过程没执行完呢?
当过程 A 执行完一个工夫片,然而还没执行完时,为了不便下次接着执行,要保留刚刚执行完的这些数据信息,叫做「保留现场」。
而后等下次再抢到了资源执行的时候,先「复原现场」,再开始继续执行。
这样周而复始。。
这样重复的保留啊、复原啊,都是额定的开销,也会让程序执行变慢。
Q:有没有更高效的形式呢?
如果两个线程归属同一个过程,就不须要保留、复原现场了。
这就是 NIO 模型的思路,也是 NIO 模型比 BIO 模型效率高很多的起因,咱们之后再讲。
线程 thread
线程,是一个过程里的具体的执行门路,就是真正干活的。
在一个过程里,一个工夫片也只能有一个线程在执行,但因为工夫片的切换速度十分快,所以看起来就如同是同时进行的。
一个过程里至多有一个线程。比方主线程,就是咱们平时写的 main()
函数,是 用户线程 ;还有 gc
线程是 JVM 生产的,负责垃圾回收,是 守护线程。
每个线程有本人的 栈 stack
,记录该线程外面的办法互相调用的关系;
然而一个过程里的所有线程是 共用堆 heap
的。
那么不同的过程之间是不能够相互拜访内存的,每个过程有本人的 内存空间 memeory space
,也就是 虚拟内存 virtual memory
。
通过这个虚拟内存,每一个过程都感觉本人领有了整个内存空间。
虚拟内存的机制,就是屏蔽了物理内存的限度。
Q:那如果物理内存被用完了呢?
用硬盘,比方 windows 零碎的分页文件,就是把一部分虚拟内存放到了硬盘上。
相应的,此时程序运行会很慢,因为硬盘的读写速度比内存慢很多,是咱们能够感触到的慢,这就是为什么开多了程序电脑就会变卡的起因。
Q:那这个虚拟内存是有多大呢?
对于 64 位操作系统来说,每个程序能够用 64 个二进制位,也就是 2^64
这么大的空间!
如果还不分明二进制相干内容的,公众号内回复「二进制」获取相应的文章哦~
总结
总结一下,在一个工夫片里,一个 CPU 只能执行一个过程。
CPU 给某个过程分配资源后,这个过程开始运行;过程里的线程去抢占资源,一个工夫片就只有一个线程能执行,谁先抢到就是谁的。
多过程 vs 多线程
每个过程是独立的,过程 A 出问题不会影响到过程 B;
尽管线程也是独立运行的,然而一个过程里的线程是共用同一个堆,如果某个线程 out of memory
,那么这个过程里所有的线程都完了。
所以多过程可能进步零碎的容错性 fault tolerance
,而多线程最大的益处就是线程间的通信十分不便。
过程之间的通信须要借助额定的机制,比方过程间通信 interprocess communication
– IPC
,或者网络传递等等。
如何创立线程
下面说了一堆概念,接下来咱们看具体实现。
Java 中是通过 java.lang.Thread
这个类来实现多线程的性能的,那咱们先来看看这个类。
从文档中咱们能够看到,Thread
类是间接继承 Object
的,同时它也是实现了 Runnable
接口。
官网文档里也写明了 2 种创立线程的形式:
一种形式是从 Thread
类继承,并重写 run()
,run()
办法里写的是这个线程要执行的代码;
启动时通过 new
这个 class
的一个实例,调用 start()
办法启动线程。
二是实现 Runnable
接口,并实现 run()
,run()
办法里同样也写的是这个线程要执行的代码;
稍有不同的是启动线程,须要 new
一个线程,并把刚刚创立的这个实现了 Runnable
接口的类的实例传进去,再调用 start()
,这其实是 代理模式。
如果面试官问你,还有没有其余的,那还能够说:
- 实现
Callable
接口; - 通过线程池来启动一个线程。
但其实,用线程池来启动线程时也是用的前两种形式之一创立的。
这两种形式在这里就不细说啦,咱们具体来看前两种形式。
继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {for (int i = 0; i < 100; i++) {System.out.println("小齐 666:" + i);
}
}
public static void main(String[] args) {MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 100; i++) {System.out.println("主线程" + i + ":齐姐 666");
}
}
}
在这里,
main
函数是主线程,是程序的入口,执行整个程序;- 程序开始执行后先启动了一个新的线程
myThread
,在这个线程里输入“小齐”; - 主线程并行执行,并输入“主线程 i:齐姐”。
来看下后果,就是两个线程交替夸我嘛~
Q:为啥和我运行的后果不一样?
多线程中,每次运行的后果可能都会不一样,因为咱们无奈人为管制哪条线程在什么时刻先抢到资源。
当然了,咱们能够给线程加上优先级 priority
,但高优先级也无奈保障这条线程肯定能先被执行,只能说有更大的概率抢到资源先执行。
实现 Runnable 接口
这种形式用的更多。
public class MyRunnable implements Runnable {
@Override
public void run() {for(int i = 0; i < 100; i++) {System.out.println("小齐 666:" + i);
}
}
public static void main(String[] args) {new Thread(new MyRunnable()).start();
for(int i = 0; i < 100; i++) {System.out.println("主线程" + i + ":齐姐 666");
}
}
}
后果也差不多:
像前文所说,这里线程启动的形式和方才的稍有不同,因为新建的的这个类只是实现了 Runnable
接口,所以还须要一个线程来“代理”执行它,所以须要把咱们新建的这个类的实例传入到一个线程里,这里其实是 代理模式。这个设计模式之后再细讲。
小结
最初提供收费的 Java 架构学习材料,学习技术内容蕴含有:Spring,Dubbo,MyBatis, RPC, 源码剖析,高并发、高性能、分布式, 性能优化,微服务 高级架构开发等等。须要的敌人点击这里备注思否自行下载即可,心愿对你们有帮忙!
还有 Java 外围知识点 + 全套架构师学习材料和视频 + 一线大厂面试宝典 + 面试简历模板能够支付 + 阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题 +Spring 源码合集 +Java 架构实战电子书。