当前位置: 首页 > news >正文

建网站哪个好 优帮云呼和浩特网站seo优化方案

建网站哪个好 优帮云,呼和浩特网站seo优化方案,wordpress更改链接地址,西安市住房和城乡建设局官网每个时代#xff0c;都不会亏待会学习的人大家好#xff0c;我是yes。本来打算继续写消息队列的东西的#xff0c;但是最近在带新同事#xff0c;发现新同事对于锁这方面有一些误解#xff0c;所以今天就来谈谈“锁”事和 Java 中的并发安全容器使用有哪些注意点。不过在这…每个时代都不会亏待会学习的人大家好我是yes。本来打算继续写消息队列的东西的但是最近在带新同事发现新同事对于锁这方面有一些误解所以今天就来谈谈“锁”事和 Java 中的并发安全容器使用有哪些注意点。不过在这之前还是得先来盘一盘为什么需要锁这玩意这得从并发 BUG 的源头说起。并发 BUG 的源头这个问题我 19 年的时候写过一篇文章 现在回头看那篇文章真的是羞涩啊。让我们来看下这个源头是什么我们知道电脑有CPU、内存、硬盘硬盘的读取速度最慢其次是内存的读取内存的读取相对于 CPU 的运行又太慢了因此又搞了个CPU缓存L1、L2、L3。正是这个CPU缓存再加上现在多核CPU的情况产生了并发BUG。这就一个很简单的代码如果此时有线程 A 和线程 B 分别在 CPU - A 和 CPU - B 中执行这个方法它们的操作是先将 a 从主存取到 CPU 各自的缓存中此时它们缓存中 a 的值都是 0。然后它们分别执行 a此时它们各自眼中 a 的值都是 1之后把 a 刷到主存的时候 a 的值还是1这就出现问题了明明执行了两次加一最终的结果却是 1而不是 2。这个问题就叫可见性问题。在看我们 a 这条语句我们现在的语言都是高级语言这其实和语法糖很类似用起来好像很方便实际上那只是表面真正需要执行的指令一条都少不了。高级语言的一条语句翻译成 CPU 指令的时候可不止一条 就例如 a 转换成 CPU 指令至少就有三条。把 a 从内存拿到寄存器中在寄存器中 1将结果写入缓存或内存中所以我们以为 a 这条语句是不可能中断的是具备原子性的而实际上 CPU 可以能执行一条指令时间片就到了此时上下文切换到另一个线程它也执行 a。再次切回来的时候 a 的值其实就已经不对了。这个问题叫做原子性问题。并且编译器或解释器为了优化性能可能会改变语句的执行顺序这叫指令重排最经典的例子莫过于单例模式的双重检查了。而 CPU 为了提高执行效率还会乱序执行例如 CPU 在等待内存数据加载的时候发现后面的加法指令不依赖前面指令的计算结果因此它就先执行了这条加法指令。这个问题就叫有序性问题。至此已经分析完了并发 BUG 的源头即这三大问题。可以看到不管是 CPU 缓存、多核 CPU 、高级语言还是乱序重排其实都是必要的存在所以我们只能直面这些问题。而解决这些问题就是通过禁用缓存、禁止编译器指令重排、互斥等手段今天我们的主题和互斥相关。互斥就是保证对共享变量的修改是互斥的即同一时刻只有一个线程在执行。而说到互斥相信大家脑海中浮现的就是锁。没错我们今天的主题就是锁锁就是为了解决原子性问题。锁说到锁可能 Java 的同学第一反应就是 synchronized 关键字毕竟是语言层面支持的。我们就先来看看 synchronized有些同学对 synchronized 理解不到位所以用起来会有很多坑。synchronized 注意点我们先来看一份代码这段代码就是咱们的涨工资之路最终百万是洒洒水的。而一个线程时刻的对比着我们工资是不是相等的。我简单说一下IntStream.rangeClosed(1,1000000).forEach可能有些人对这个不太熟悉这个代码的就等于 for 循环了100W次。你先自己理解下看看觉得有没有什么问题第一反应好像没问题你看着涨工资就一个线程执行着这比工资也没有修改值看起来好像没啥毛病没有啥并发资源的竞争也用 volatile 修饰了保证了可见性。让我们来看一下结果我截取了一部分。可以看到首先有 log 打出来就已经不对了其次打出来的值竟然还相等有没有出乎你的意料之外有同学可能下意识就想到这就raiseSalary在修改所以肯定是线程安全问题来给raiseSalary 加个锁请注意只有一个线程在调用raiseSalary方法所以单给raiseSalary方法加锁并没啥用。这其实就是我上面提到的原子性问题想象一下涨工资线程在执行完yesSalary还未执行yourSalary时比工资线程刚好执行到yesSalary ! yourSalary 是不是肯定是 true 所以才会打印出 log。再者由于用 volatile 修饰保证了可见性所以当打 log 的时候可能yourSalary已经执行完了这时候打出来的 log 才会是yesSalary yourSalary。所以最简单的解决办法就是把raiseSalary() 和 compareSalary() 都用 synchronized 修饰这样涨工资和比工资两个线程就不会在同一时刻执行因此肯定就安全了看起来锁好像也挺简单不过这个 synchronized 的使用还是对于新手来说还是有坑的就是你要关注 synchronized 锁的究竟是什么。比如我改成多线程来涨工资。这里再提一下parallel这个其实就是利用了 ForkJoinPool 线程池操作默认线程数是 CPU 核心数。由于 raiseSalary() 加了锁所以最终的结果是对的。这是因为 synchronized 修饰的是yesLockDemo实例我们的 main 中只有一个实例所以等于多线程竞争的是一把锁所以最终计算出来的数据正确。那我再修改下代码让每个线程自己有一个 yesLockDemo 实例来涨工资。你会发现这锁怎么没用了这说好的百万年薪我就变 10w 了这你还好还有 70w。这是因为此时我们的锁修饰的是非静态方法是实例级别的锁而我们为每个线程都创建了一个实例因此这几个线程竞争的就根本不是一把锁而上面多线程计算正确代码是因为每个线程用的是同一个实例所以竞争的是一把锁。如果想要此时的代码正确只需要把实例级别的锁变成类级别的锁。很简单只需要把这个方法变成静态方法synchronized 修饰静态方法就是类级别的锁。还有一种就是声明一个静态变量比较推荐这种因为把非静态方法变成静态方法其实就等于改了代码结构了。我们来小结一下使用 synchronized 的时候需要注意锁的到底是什么如果修饰静态字段和静态方法那就是类级别的锁如果修饰非静态字段和非静态方法就是实例级别的锁。锁的粒度相信大家知道 Hashtable 不被推荐使用要用就用 ConcurrentHashMap是因为 Hashtable 虽然是线程安全的但是它太粗暴了它为所有的方法都上了同一把锁我们来看下源码。你说这 contains 和 size 方法有啥关系 我在调用 contains 的时候凭啥不让我调 size ? 这就是锁的粒度太粗了我们得评估一下不同的方法用不同的锁这样才能在线程安全的情况下再提高并发度。但是不同方法不同锁还不够的因为有时候一个方法里面有些操作其实是线程安全的只有涉及竞争竞态资源的那一段代码才需要加锁。特别是不需要锁的代码很耗时的情况就会长时间占着这把锁而且其他线程只能排队等着比如下面这段代码。很明显第二段代码才是正常的使用锁的姿势不过在平时的业务代码中可不是像我代码里贴的 sleep 这么容易一眼就看出的有时候还需要修改代码执行的顺序等等来保证锁的粒度足够细。而有时候又需要保证锁足够的粗不过这部分JVM会检测到它会帮我们做优化比如下面的代码。可以看到明明是一个方法里面调用的逻辑却经历了加锁-执行A-解锁-加锁-执行B-解锁很明显的可以看出其实只需要经历加锁-执行A-执行B-解锁。所以 JVM 会在即时编译的时候做锁的粗化将锁的范围扩大类似变成下面的情况。而且 JVM 还会有锁消除的动作通过逃逸分析判断实例对象是线程私有的那么肯定是线程安全的于是就会忽略对象里面的加锁动作直接调用。读写锁读写锁就是我们上面提交的根据场景减小锁的粒度了把一个锁拆成了读锁和写锁特别适合在读多写少的情况下使用例如自己实现的一个缓存。ReentrantReadWriteLock读写锁允许多个线程同时读共享变量但是写操作是互斥的即写写互斥、读写互斥。讲白了就是写的时候就只能一个线程写其他线程也读不了也写不了。我们来看个小例子里面也有个小细节。这段代码就是模拟缓存的读取先上读锁去缓存拿数据如果缓存没数据则释放读锁再上写锁去数据库取数据然后塞入缓存中返回。这里面的小细节就是再次判断 data getFromCache() 是否有值因为同一时刻可能会有多个线程调用getData()然后缓存都为空因此都去竞争写锁最终只有一个线程会先拿到写锁然后将数据又塞入缓存中。此时等待的线程最终一个个的都会拿到写锁获取写锁的时候其实缓存里面已经有值了所以没必要再去数据库查询。当然 Lock 的使用范式大家都知道需要用 try- finally来保证一定会解锁。而读写锁还有一个要点需要注意也就是说锁不能升级。什么意思呢我改一下上面的代码。但是写锁内可以再用读锁来实现锁的降级有些人可能会问了这写锁都加了还要什么读锁。还是有点用处的比如某个线程抢到了写锁在写的动作要完毕的时候加上读锁接着释放了写锁此时它还持有读锁可以保证能马上使用写锁操作完的数据而别的线程也因为此时写锁已经没了也能读数据。其实就是当前已经不需要写锁这种比较霸道的锁所以来降个级让大家都能读。小结一下读写锁适用于读多写少的情况无法升级但是可以降级。Lock 的锁需要配合 try- finally来保证一定会解锁。对了我再稍稍提一下读写锁的实现熟悉 AQS 的同学可能都知道里面的 state 读写锁就是将这个 int 类型的 state 分成了两半高 16 位与低 16 位分别记录读锁和写锁的状态。它和普通的互斥锁的区别就在于要维护这两个状态和在等待队列处区别处理这两种锁。所以在不适用于读写锁的场景还不如直接用互斥锁因为读写锁还需要对state进行位移判断等等操作。StampedLock这玩意我也稍微提一下是 1.8 提出来的出镜率似乎没有 ReentrantReadWriteLock 高。它支持写锁、悲观读锁和乐观读。写锁和悲观读锁其实和 ReentrantReadWriteLock 里面的读写锁是一致的它就多了个乐观读。从上面的分析我们知道读写锁在读的时候其实是无法写的而 StampedLock 的乐观读则允许一个线程写。乐观读其实就是和我们知道的数据库乐观锁一样数据库的乐观锁例如通过一个version字段来判断例如下面这条 sql。StampedLock 乐观读就是与其类似我们来看一下简单的用法。它与 ReentrantReadWriteLock 对比也就强在这里其他的不行比如 StampedLock 不支持重入不支持条件变量。还有一点使用 StampedLock 一定不要调用中断操作因为会导致CPU 100%我跑了下并发编程网上面提供的例子复现了。具体的原因这里不再赘述文末会贴上链接上面说的很详细了。所以出来一个看似好像很厉害的东西你需要真正的去理解它熟悉它才能做到有的放矢。CopyOnWrite写时复制的在很多地方也会用到比如进程 fork() 操作。对于我们业务代码层面而言也是很有帮助的在于它的读操作不会阻塞写写操作也不会阻塞读。适用于读多写少的场景。例如 Java 中的实现 CopyOnWriteArrayList有人可能一听这玩意线程安全读的时候还不会阻塞写好家伙就用它了你得先搞清楚写时复制是会拷贝一份数据你的任何一个修改动作在CopyOnWriteArrayList 中都会触发一次Arrays.copyOf然后在副本上修改。假如修改的动作很多并且拷贝的数据也很大这将是灾难并发安全容器最后再来谈一下并发安全容器的使用我就拿相对而言大家比较熟悉的 ConcurrentHashMap 来作为例子。我看新来的同事好像认为只要是使用并发安全容器一定就是线程安全了。其实不尽然还得看怎么用。我们先来看下以下的代码简单的说就是利用 ConcurrentHashMap 来记录每个人的工资最多就记录 100 个。最终的结果都会超标即 map 里面不仅仅只记录了100个人。那怎么样结果才会是对的很简单就是加个锁。看到这有人说你这都加锁了我还用啥 ConcurrentHashMap 我 HashMap 加个锁也能完事是的你说的没错因为当前我们的使用场景是复合型操作也就是我们先拿 map 的 size 做了判断然后再执行了 put 方法ConcurrentHashMap 无法保证复合型的操作是线程安全的!而 ConcurrentHashMap 合适只是用其暴露出来的线程安全的方法而不是复合操作的情况下。比如以下代码当然我这个例子不够恰当其实因为 ConcurrentHashMap 性能比 HashMap 锁高的原因在于分段锁需要多个 key 操作才能体现出来不过我想突出的重点是使用的时候不能大意不能纯粹的认为用了就线程安全了。总结一下今天谈了谈并发 BUG 的源头即三大问题可见性问题、原子性问题和有序性问题。然后简单的说了下 synchronized 关键字的注意点即修饰静态字段或者静态方法是类层面的锁而修饰非静态字段和非静态方法是实例层面的类。再说了下锁的粒度在不同场景定义不同的锁不能粗暴的一把锁搞定并且方法内部锁的粒度要细。例如在读多写少的场景可以使用读写锁、写时复制等。最终要正确的使用并发安全容器不能一味的认为使用并发安全容器就一定线程安全了要注意复合操作的场景。当然我今天只是浅浅的谈了一下关于并发编程其实还有很多点要写出线程安全的代码不是一件容易的事情就像我之前分析的 Kafka 事件处理全流程一样原先的版本就是各种锁控制并发安全到后来bug根本修不动多线程编程难调试也难修bug也难。因此 Kafka 事件处理模块最终改成了单线程事件队列模式将涉及到共享数据竞争相关方面的访问抽象成事件将事件塞入阻塞队列中然后单线程处理。所以在用锁之前我们要先想想有必要么能简化么不然之后维护起来有多痛苦到时候你就知道了。最后之后继续开始写消息队列相关的包括 RocketMQ 和 Kafka有不少同学在后台留言想和我深入的交流一下发生点关系我把公众号菜单加了个联系我有需求的小伙伴可以加我微信。扫码可关注我的公众号哦~
http://www.zqtcl.cn/news/113279/

相关文章:

  • 响应式网站一般做几个版本官网+wordpress
  • 太原网站建设方案服务佛山市建设工程有限公司
  • 智能网站建设平台php mysql 网站源码
  • 夏天做那些网站能致富百度关键词价格怎么查询
  • 厦门微信网站专业从事网站开发公司
  • 网站标题的写法湖南如何做网络营销
  • 设计做兼职的网站求推荐医院英文网站建设
  • 有没得办法可以查询一个网站有没得做竞价呀ai可以用来做网站吗
  • 俄乌局势最新消息惠州seo排名优化
  • 常州发布信息的有什么网站电商平台建设公司
  • 高新区手机网站建设长沙关键词优化服务
  • 网站开发预算报价表推销网站的方法
  • 做网站需要几个人昆明旅行社网站开发
  • 上海产品网站建设网站建设分为哪些
  • 史志网站建设在线网站建设工程标准
  • 青海省建设工程在哪个网站发布北京专业网站外包公司
  • 东营网站建设公司wordpress获取子分类
  • 网站的尾页要怎么做d代码做网站
  • 自己做一元购网站烟台网站设计公司推荐
  • 有没有做彩票直播的网站成都十八个网红打卡地
  • 急求聊城网站建设网站服务器管理系统
  • 做网站需要什么许可证商场设计效果图
  • html网页制作视频windows优化大师有哪些功能
  • 国外建站主机帝国手机网站cms系统
  • 响应式网站建设哪家好网站空间支付方式
  • 腾讯广告建站工具贵州企业网站建设价格
  • 最新的网站建设架构wordpress管理员头像
  • 手机网站模版化工网站建设公司
  • 网站建设 会计分录北京网站建设主页
  • 北京市建设监理协会网站网站一般多少钱