多线程和单线程有什么区别,多线程编程要注意什么,多线程加锁要注意什么

嗯……当我在 2 02 2 年从事那个项目时,我真的被难住了。
单线程与多线程......差异巨大。

单线就是穿过孔的单线。
到 2 02 2 年,随着用户数量和应用程序访问量的增加,单个线程将被卡住。
想一想。
它的速度非常慢,因为一个请求在发送下一个请求之前就已被接收和处理。
在服务器端,只有两三个CPU核心在单线程运行,核心利用率低得可怕。
后来我意识到单线程是顺序运行的。
只有完成一项任务后才能开始下一项任务。

多线程是指多条线程同时穿过多个孔。
2 02 2 年,电商网站推出闪购活动,并走向多线程。
效果很明显。
当有很多用户请求时,多个线程可以同时处理它们,从而提高速度。
使用四核 CPU 并运行多个线程肯定会对性能产生影响。
然而,记忆力却很紧张。
同时运行多个线程会消耗大量内存。
2 02 2 年的服务器配备 8 G 内存。
如果运行太多,就会导致OOM,直接崩溃。
而且写起来很痛苦。
单线程,逻辑简单,读,写,写,没问题。
多线程意味着数据被多个线程使用。
如何确保数据没有改变?线程安全、死锁,这些讨厌的事情需要时间来处理。
当时我很困惑,但是随着看书、查资料,我逐渐明白同步机制和互斥锁是必要的。

加锁就更让人头疼了。
2 02 2 项目锁定不当,卡住,程序卡住,用户投诉较多。
锁的管理、锁的获取、锁的释放必须保证顺序,不能有错误。
锁本身有性能开销。
每次都要跟它打架,占用CPU时间。
因此,尽可能避免使用锁。
一旦使用,就应该进行优化。
例如,当读多写少时,读/写锁可以通过允许多个读取线程同时读取而无需一次排队一个来提高性能。
自旋锁可以防止线程直接休眠并旋转一圈等待锁。
这可能比从睡眠中醒来更快。
然而,自旋锁也浪费了,所以要根据情况来使用。
此外,锁必须是细粒度的。
对于大型数据结构,所有线程都必须获取锁,导致大家都在等待,效率低下。
它可以被分割成更小的块,更小的块使用更小的锁。
这样,如果一个线程修改一个小块,其他线程也可以修改其他块。
原子操作等无锁数据结构也是一个方向,但实现起来比较复杂。

所以单线程很简单,但如果发生什么事情你就会陷入困境。
虽然是多线程,性能很高,但是比较麻烦,需要仔细处理各种问题。
2 02 2 年,我可能有点偏激,但我觉得多线程是个好东西,但是效果不好,问题很多。
您应该根据您的实际情况选择合适的计划。

深入理解多线程(五)—— Java虚拟机的锁优化技术

那天晚上我调试代码到凌晨两点,屏幕上CPU使用率指示灯闪烁。
明明只有三个线程在运行,但是监控工具显示线程频繁卡在锁上,醒来时CPU电流像心电图一样跳动。
突然我意识到问题可能出在同步块上,这些看似合理的锁实际上正在成为性能瓶颈。

自旋锁的原理还是蛮有趣的。
我记得JDK 1 .6 刚发布的时候,我写了一个高并发计数器。
在测试过程中,我注意到该线程在自旋阶段占用了近3 0%的核心资源。
当时的系统是2 01 2 年的Mac Pro,单核运行能力甚至比今天的笔记本电脑还要强大。
后来采用CAS操作进行加锁,直接降低了CPU负载。
但有一个奇怪的事情。
当计数器更新操作特别频繁时,旋转会减慢速度。

解除锁的例子就更神奇了。
有一次,当我重构旧代码时,我发现方法中的同步块仅保护局部变量。
编译后,我用JProfiler检查,发现同步被删除了!但是当我将局部变量更改为全局对象时,自动添加了同步。
这种优化现在看起来很聪明,但当我调试它时,我几乎认为 JIT 编译器有问题。

最常见的锁放大场景是批量操作。
我写了一个处理Excel文件的工具。
在原始代码中,每个单元格的更新都是被锁定的。
优化后,整个sheet的读写直接打包成一个同步块。
结果,CPU 利用率从峰值 8 5 % 下降到 5 2 %,内存页面错误也减少了。
然而,有一个副作用。
当sheet特别大时,JVM会提前触发Full GC,这又是一个新的陷阱。

等等,还有一件事。
旋转锁有一个隐藏条件,即锁紧时间要足够短。
我在单核CPU上进行了测试,当锁保​​持时间超过5 毫秒时,旋转比阻塞更糟糕。
这与当时使用的Xeon E5 2 6 5 0有关。
当时睿频技术尚未成熟。
但如果升级到当前的 Cortex-A9 ,该阈值可能需要加倍。

我突然想到这些优化其实是一个悖论。
就像给自行车添加电力一样,它省力但很容易忽视机械结构的局限性。
今天的 JVM 可能比那时聪明很多,但是程序员不也应该知道什么时候“拧紧螺丝”、什么时候“卸下辅助轮”吗?

面试官问:Java 中的锁有哪些?我跪了……

等等,还有一件事,我上次写代码的时候就遇到了线程死锁的情况。
当时我用的是同步锁。
结果,由于调用顺序错误,两个线程持有彼此所需的锁,最终陷入困境。
当时确实很迷茫。
我花了很长时间才找到问题所在。
我改成ReentrantLock,并指定公平锁,问题就解决了。
正如您所看到的,锁的选择非常重要。

有锁无锁是什么意思?

这种带锁和不带锁的多线程编程可能看起来有点复杂,但是说白了,都是一样的东西。

当时,我正在某个城市做一个项目。
假设现在是 2 02 2 年,我必须使用锁定和解锁方法来处理数据同步问题。
然后我看到有一把锁。
很简单。
这是一系列锁定和解锁代码,以防止数据混乱。

你需要考虑无锁。
这是通过原子操作或乐观锁定来实现的,这听起来相当先进。
虽然这个功能性能不错,但是实现起来却很困难。

我当时很迷茫,我该怎么选择?后来我想了想,才发现有一把锁。
它很简单,但会影响性能。
嗯,对于一台几百元的服务器来说,性能还不错。
Lock-Free实现起来比较困难,但是性能很好。
我们的项目需要处理海量的数据,几千万条信息。
没有锁定的声音更合适。

后来项目上线了,反响还不错,我就放心了。
但是哦,我可能有点极端。
后来我想这两种方法还是要根据具体情况而定。
有的地方锁坚固,有的地方锁坚固。
嘿,就是这样。