加入收藏 | 设为首页 | 会员中心 | 我要投稿 辽源站长网 (https://www.0437zz.com/)- 云专线、云连接、智能数据、边缘计算、数据安全!
当前位置: 首页 > 服务器 > 搭建环境 > Unix > 正文

Java多线程优化都不会,怎么拿Offer?

发布时间:2020-01-07 21:28:51 所属栏目:Unix 来源:站长网
导读:副标题#e# 【51CTO.com原创稿件】随着业务量的增加,多线程处理成为家常便饭。于是,多线程优化成了摆在我们面前的问题。Java 作为当今主流的应用开发语言,也会有同样的问题。 图片来自 Pexels 今天,我们从 Java 内部锁优化,代码中的锁优化,以及线程池

这种方式虽然不会带来上下文的切换,但是会消耗 CPU 的资源。为了综合较长和较短两种线程等待模式,JVM 会根据运行过程中收集到的信息来判断,锁持有时间是较长时间或者较短时间。然后再采取线程暂停或忙等的策略。

Java 代码中如何进行锁优化

前面讲了 Java 系统是如何针对内部锁进行优化的。如果说内部锁的优化是 Java 系统自身完成的话,那么接下来的优化就需要通过代码实现了。

锁的开销主要是在争用锁上,当多线程对共享资源进行访问时,会出现线程等待。

即便是使用内存屏障,也会导致冲刷写缓冲器,清空无效化队列等开销。

为了降低这种开销,通常可以从几个方面入手,例如:减少线程申请锁的频率(减少临界区)和减少线程持有锁的时间长度(减小锁颗粒)以及多线程的设计模式。

减少临界区的范围

当共享资源需要被多线程访问时,会将共享资源或者代码段放到临界区中。

如果在代码书写中减少临界区的长度,就可以减少锁被持有的时间,从而降低锁被征用的概率,达到减少锁开销的目的。

Java多线程优化都不会,怎么拿Offer?

减少临界区示例图

如上图,尽量避免对一个方法进行加锁同步,可以只针对方法中的需要同步资源/变量进行同步。其他的代码段不放到 Synchronzied 中,减少临界区的范围。

减小锁的颗粒度

减小锁的颗粒度可以降低锁的申请频率,从而减小锁被争用的概率。其中一种常见的方法就是将一个颗粒度较粗的锁拆分成颗粒度较细的锁。

Java多线程优化都不会,怎么拿Offer?

拆分锁的颗粒度

假设有一个类 ServerStatus,里面包含了四个方法:

addUser

addQuery

removeUser

removeQuery

如果分别在每个方法加上 Synchronized。在一个线程访问其中任意一个方法的时候,将锁住 ServerStatus,此时其他线程都无法访问另外三个方法,从而进入等待。

Java多线程优化都不会,怎么拿Offer?

如果只针对每个方法内部操作的对象加锁,例如:addUser 和 removeUser 方法针对 users 对象加锁。又例如:addQuery 和 removeQuery 方法针对 queries 对象加锁。

假设,当一个线程池调用 addUser 方法的时候,只会锁住 user 对象。另外一个线程是可以执行 addQuery 和 removeQuery 方法的。

并不会因为锁住整个对象而进入等待。JDK 内置的 ConcurrentHashMap 与 SynchronizedMap 就使用了类似的设计。

Java多线程优化都不会,怎么拿Offer?

针对不同的方法中使用的对象进行锁定

读写锁

也叫做线程的读写模式(Read-Write Lock),其本质是一种多线程设计模式。

将读取操作和写入操作分开考虑,在执行读取操作之前,线程必须获取读取的锁。

在执行写操作之前,必须获取写锁。当线程执行读取操作时,共享资源的状态不会发生变化,其他的线程也可以读取。但是在读取时,不可以写入。

其实,读写模式就是将原来共享资源的锁,转化成为读和写两把锁,将其分两种情况考虑。

如果都是读操作可以支持多线程同时进行,只有在写时其他线程才会进入等待。

Java多线程优化都不会,怎么拿Offer?

Reader 线程正在读取,Writer 线程正在等待

Java多线程优化都不会,怎么拿Offer?

Writer 线程正在写入,Reader 线程正在等待

Java多线程优化都不会,怎么拿Offer?

读写锁类图

说完了读写锁的基本原理,再来看看参与的角色:

Reader(读者),对 SharedResource 角色执行 Read 操作。

Writer(写者),对 SharedResource 角色执行 Write 操作。

SharedResource(共享资源),表示对 Reader 和 Writer 两者共享的资源。

ReadWriteLock(读写锁),提供了 SharedResource 角色实现 Read 操作和 Write 操作时所需的锁。

针对 Read 操作提供 readLock 和 readUnlock,对 Write 操作提供 writeLock 和 writeUnlock。

特别需要注意的是,在这里需要解决读写冲突的问题。当线程 A 获取读锁时,如果有线程 B 正在执行写操作,线程 A 需要等待,否则会引起 read-write conflict(读写冲突)。

如果线程 B 正在执行读操作,线程 A 不需要等待,因为 read-read 不会引起 conflict(冲突)。

当线程 A 要获取写入锁时,线程 B 正在执行写操作,线程 A 需要等待,否则会引起 write-write conflict(写写冲突)。

如果线程 B 正在执行读操作,则线程 A 需要等待,否则会引起 read-write conflict(读写冲突)。

(编辑:辽源站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读