16 | 多线程调优(下):如何优化多线程上下文切换?
该思维导图由 AI 生成,仅供参考
竞争锁优化
- 深入了解
- 翻译
- 解释
- 总结
本文介绍了如何优化多线程上下文切换的方法,主要包括竞争锁优化和wait/notify优化两个方面。在竞争锁优化方面,文章提到了减少锁的持有时间、降低锁的粒度和非阻塞乐观锁替代竞争锁等方法。在wait/notify优化方面,文章指出了使用Object.notify()替代Object.notifyAll()、尽快释放内部锁以及使用Lock锁结合Condition接口替代Synchronized内部锁中的wait/notify等方法。此外,文章还提到了合理设置线程池大小、使用协程实现非阻塞等待以及减少Java虚拟机的垃圾回收等优化方法。总的来说,本文通过介绍竞争锁优化和wait/notify优化的方法,为读者提供了在多线程编程中优化上下文切换的实用技巧,有助于提高系统的性能和并发能力。
《Java 性能调优实战》,新⼈⾸单¥59
全部留言(38)
- 最新
- 精选
- Geek_1f1a07Zed说的不对,首先,所有的锁,无论synchronize还是lock,如果发生竞争条件,都可能造成上下文切换,优化锁的目的是为了尽量降低发生锁竞争的概率,synchronize做的优化都是把竞争的可能消灭在前期的偏向锁,轻量级锁,把会造成上下文切换的“脏活”留在最后。lock的乐观锁大体思路也是一样的,不到万不得已,不会轻易调用park方法。但是本质上java目前都是利用内核线程,所以都会有上下文切换。除非使用协程的技术,这个以前有green thread,后来不用了,期待老师后面对协程的讲解。
作者回复: 回答很好,赞一个。
2019-06-2557 - 尔冬橙volitile的读写不会导致上下文切换,操作系统层面怎么理解呢
作者回复: volatile主要是用来保证共享变量额可见性,以及防止指令重排序,保证执行的有序性。 通过生成.class文件之后,反编译文件我们可以看到通过volatile修饰的共享变量,在写入操作的时候会多一个Lock前缀这样的指令,当操作系统执行时会由于这个指令,将当前处理器缓存的数据写回系统内存中,并通知其他处理器中的缓存失效。 所以volatile不会带来线程的挂起操作,不会导致上下文切换。
2019-09-2118 - Zed回答趙衍同学 如你所说,synchronized主要是因为有用户态和内核态的交互所以能到进程级别。 而Lock是通过AQS的state状态来判断是否持有锁,整个过程都是在用户态或者说纯java实现。 最后lock.await()也是把当前线程放到当前条件变量的等待队列中并让出cpu。顺便提下,lock支持多条件变量。
作者回复: 回答很好。线程进入阻塞,两者都会发生进程上下文切换。Synchronized中阻塞线程无论何时去获取锁,都需要进入到内核态,而AQS中,阻塞线程再次获取锁时,是通过state以及CAS操作判断,只有没有竞争成功时,才会再次被挂起,这样可以尽量减少上下文切换。
2019-06-2515 - QQ怪我觉得有些人建议使用notifyall的原因是使用notify需要有十足的把握去确认哪条线程需要唤醒,因为一不留神就容易搞错,为了优化而优化最后事倍功半,所以大家才会使用notifyall一劳永逸,我其实挺认同老师的观点,老师,全部唤醒会导致更多的上下文切换,是否要优化这点,我觉得还是得看个人了吧😂
作者回复: notify()可以结合wait(long)方法使用,解决某些没有通知的线程被通知不到的问题
2019-06-26313 - 你好旅行者老师好!在synchronized中,“挂起”这个动作是由JVM来实现的,获取不到锁的线程会被迫让出CPU,由于synchronized是基于操作系统的mutex机制,所以会产生进程的上下文切换。我想请问老师,在JDK的Lock中,或者AQS中,线程“挂起”这个动作又是怎么实现的呢?为什么不会产生进程级别的上下文切换呢?
作者回复: AQS挂起是通过LockSupport中的park进入阻塞状态,这个过程也是存在进程上下文切换的。但被阻塞的线程再次获取锁时,不会产生进程上下文切换,而synchronized阻塞的线程每次获取锁资源都要通过系统调用内核来完成,这样就比AQS阻塞的线程更消耗系统资源了。
2019-06-25210 - td901105老师,是不是使用Lock锁机制不会有用户态和内核态的切换?还是Lock本身锁机制是不涉及用户态到内核态的切换的,只是在未获取锁的时候需要使用内核态的方法比如park方法进行线程的挂起?
作者回复: 一样会有,相对同步锁来说,只是减少了用户态和内核态的切换。Lock锁被阻塞的线程再次获取锁时,不会产生进程上下文切换,而synchronized阻塞的线程每次获取锁资源都要通过系统调用内核来完成。
2019-12-205 - K老师好,我有个特别简单的小问题不太明白。既然用了vector,为什么还要用synchronize锁起来啊,vector本身不就是线程安全的?谢谢老师回答。
作者回复: 这里的vector是一个对象锁,锁的是一个代码块,并不是保证vector的线程安全。
2019-07-2225 - WL老师请问一下, JVM在操作系统层面是一个进程还是多个进程, 如果是一个进程的话, 那synchronize和park()方法发生的是进程级别的状态切换的话是指操作系统不运行JVM了吗?
作者回复: 一个JVM在操作系统中只有一个进程,这里指的是进程中的某个运行的线程停止使用CPU,切换到内核获取CPU运行,而不是说停止JVM,然后运行内核。这里的切换是用户态使用CPU切换到了内核态使用CPU。
2019-06-275 - 皮皮老师您好,一直有个疑问想请教,就是JDK1.5引入的lock锁底层实现也是调用了lockhelper的park和unpark方法,这个是否也涉及到系统的上下文切换,用户态和内核态的切换?
作者回复: 是的
2019-06-254 - 奇奇代码写错了 while(pool.isEmpty())不能放在同步代码块的外面 假设此时pool不为空容量为1,此时10个线程的pool.isEmpty都为false,此时全部跳出循环。 全部执行pool.remove(0) 错误
编辑回复: 同学你好!后面有个锁,不会同时进去remove。如有疑问,可继续留言。
2019-07-023