前言
文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger
种一棵树最好的时间是十年前,其次是现在
鼓励大家在技术的路上写博客
絮叨
记得每次面试的时候 都会问到一个问题:你来说说有哪些线程安全的类?我心里一想,这我早都背好了,稀里哗啦说了一大堆。 但是对于什么是线程安全我们真的了解吗?之前的我真的是了解甚微,那么我们今天就来聊聊这个问题。
Tips 小六六今天先带大家来了解一下线程安全,然后我再抬杠一下线程安全
什么是进程
电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如下图中IDEA,Navicat,PostMan 这些
什么是线程?
进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。
那什么是多线程?提到多线程这里要说两个概念,就是串行和并行,搞清楚这个,我们才能更好地理解多线程。
所谓串行,其实是相对于单条线程来执行多个任务来说的,我们就拿下载文件来举个例子:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,也就是说,必须等下载完A之后才能开始下载B,它们在时间上是不可能发生重叠的。
并行:下载多个文件,开启多条线程,多个文件同时进行下载,这里是严格意义上的,在同一时刻发生的,并行在时间上是重叠的。
了解了这两个概念之后,我们再来说说什么是多线程。举个例子,我们打开腾讯管家,腾讯管家本身就是一个程序,也就是说它就是一个进程,它里面有很多的功能,我们可以看下图,能查杀病毒、清理垃圾、电脑加速等众多功能。
什么是线程安全?
既然是线程安全问题,那么毫无疑问,所有的隐患都是在多个线程访问的情况下产生的,也就是我们要确保在多条线程访问的时候,我们的程序还能按照我们预期的行为去执行,我们看一下下面的代码。
public class ThreadDemo {
static Integer count = 0;
public void getCount() {
count ++;
System.out.println(count+Thread.currentThread().getName());
}
public static void main(String[] args) {
for (int i=0;i<=3;i++){
new Thread(()->{
for (int a=0;a<=10;a++){
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.getCount();
}
}).start();
}
"C:\Program Files\Java\jdk1.8.0_172\bin\java.exe" -Dvisualvm.id=77484934182900 "-javaagent:E:\manuls\IntelliJ IDEA 2019.3\lib\idea_rt.jar=64836:E:\manuls\IntelliJ IDEA 2019.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_172\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar;E:\code\sixfinger-leadnews\service-mycat\target\classes;E:\code\sixfinger-leadnews\service-mycat\libs\Mycat-server-1.6-RELEASE.jar;D:\repository\org\springframework\boot\spring-boot-starter-log4j2\2.1.5.RELEASE\spring-boot-starter-log4j2-2.1.5.RELEASE.jar;D:\repository\org\apache\logging\log4j\log4j-slf4j-impl\2.11.2\log4j-slf4j-impl-2.11.2.jar;D:\repository\org\slf4j\slf4j-api\1.7.26\slf4j-api-1.7.26.jar;D:\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;D:\repository\org\apache\logging\log4j\log4j-core\2.11.2\log4j-core-2.11.2.jar;D:\repository\org\apache\logging\log4j\log4j-jul\2.11.2\log4j-jul-2.11.2.jar;D:\repository\org\slf4j\jul-to-slf4j\1.7.26\jul-to-slf4j-1.7.26.jar" com.heima.ThreadDemo 1 4 5 6 7 8 9 10 11 12 13 3 14 15 2 1 17 16 19 18 21 23 24 25 26 27 28 29 20 22 30 31 32 33 35 36 37 38 34 39 41 40 42 43 Process finished with exit code 0
我们可以看到,这里出现了两个1,出现这种情况显然表明这个方法根本就不是线程安全的,出现这种问题的原因有很多。
最常见的一种,就是我们A线程在进入方法后,拿到了count的值,刚把这个值读取出来,还没有改变count的值的时候,结果线程B也进来的,那么导致线程A和线程B拿到的count值是一样的。
那么由此我们可以了解到,这确实不是一个线程安全的类,因为他们都需要操作这个共享的变量。其实要对线程安全问题给出一个明确的定义,还是蛮复杂的,我们根据我们这个程序来总结下什么是线程安全。
当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
解决线程安全
这边我就不说了,因为太多了,对于线程安全的各种解决方案啥的,这篇文章就不深入讨论了,接下来我要来聊聊我们这些CRUD仔 有真实的项目中,能有多少机会碰到线程安全问题。
抬杠线程安全
早上公司在同事看集合的源码,然后我也凑过去跟着一起探讨了一次,我发现一个问题,就是我发现我维护的系统,和我开发过的系统,我很少说要考虑线程安全问题,你比如说 ArrayList,HashMap 大家都说是线程不安全的,但是我想了很久我根本就没有看到有场景会要我去考虑线程安全问题,就是我的集合大部分我都是定义在方法内部,那定义在方法内部,我们crud仔又很少的线程内存说定义使用多线程,所以就是感觉我们这种业务开发,对于多线程的理解就是很难到达一个深刻的程度。
大家可以想一下,大家平时撸代码是不是 controller, service,dao,一层一层往下撸,一个请求进来就直接进入到一个方法中来了,也就是我们常常说的栈帧。对于栈帧里面定义的集合容器,我们也很少去定义多线程出处理,对于业务来说,是真的少,或许你会说我们会可以用在一个方法中用多线程去查询,去更新。确实是可以的,但是你想,你是不是会一开始就把你要操作的集合分配好了,每个线程对应一个集合,那么真实业务场景中,要你考虑的线程安全的地方又在哪里呢?
源码中学习
说实话,今天跟同事讨论,他说 碰到得多了 你也就懂了,他说这种东西靠的是你自己去累计,但是举例子嘛也一下子说不上,同事说我的经验太少,见的太少了,同事算是大牛吧 5年经验以上了,差不多算架构师了,我想想自己的也许是真的经验不够吧,除了在容器源码,各种Spring 源码中看到过,其他确实见到的少了,不过同事有句话话实在,我们为啥不把自己的程序的内存当数据库呢,如果你往你的应用程序中去放数据,那么你就得考虑这些东西了,比如秒杀场景 在减库存的方法之上设计一个限流算法,这些就得考虑线程安全了吧。
结尾
哈哈,笔者不是说是一个标题党,只是说自己对一些东西的思考,我觉得虽然说我们用的少,但是还是有必要去学习这些知识的,如果,你写中间件,你写一个数据库,这些都是必须考虑的东西,如果仅仅是CRUD,你确实是很少需要了解这些,但是我们要成长,还是必须得学的。
Tips 大家可以在下面留言,我看看大家得业务场景,小六六可能是真的遇到的少,只能像各位大佬学习学习。
日常求赞
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉。
创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见
六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !
今天的文章小六六抬杠线程安全分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21922.html