热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java并发基础(六)

《java并发编程实战》的第9章主要介绍GUI编程,在实际开发中实在很少见到,所以这一章的笔记暂时先放一放,从第10章开始到第12章是第三部分,也就是活跃性、性能、与测试,这部分的知识偏理论多一些

  《java并发编程实战》的第9章主要介绍GUI编程,在实际开发中实在很少见到,所以这一章的笔记暂时先放一放,从第10章开始到第12章是第三部分,也就是活跃性、性能、与测试,这部分的知识偏理论多一些,但是尽量能用代码讲明白的问题就不用文字,话不多说,进入正题。

一、死锁

  在学习java基础的时候就听老师讲过“哲学家就餐”的例子,时间久了具体是怎么回事也容易忘,这里重新整理下。5个哲学家去吃中餐,坐在一张圆桌旁,他们有5根筷子(不是5双),并且每两个人中间放一根筷子,每个人需要一双筷子才能吃到东西,如果每个人都立即抓住自己左边的筷子,并等待自己右边的筷子空出来,同时又不肯放弃自己已经拿到的筷子,那么每个人都会饿死。在java中就是,线程A持有锁L并想获得锁M的同时,线程B持有锁M并尝试获得锁L,那么会产生死锁,总结一下,也就是多个线程由于存在环路的锁依赖关系而永远等待下去,所以会产生死锁,而java程序是无法从死锁中恢复过来的,所以开发时要尤其注意。

//容易发生死锁
public class LeftRightDeadLock{
private final Object left = new Object();
private final Object right = new Object();

public void leftRight(){
synchronized(left){
synchronized (right) {
System.out.println(
"left-right");
}
}
}

public void rightLeft(){
synchronized (right) {
synchronized (left) {
System.out.println(
"right-left");
}
}
}
}

  上面这种形式是最简单的死锁,叫锁顺序死锁,它发生死锁的原因是:两个线程试图以不同顺序访问相同的锁。反过来,也就是说,如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁。但是在实际开发中几乎不可能实现。

  有些时候死锁并不那么容易被发现,如下面代码,它将资金从一个账户转入另一个账户,在开始转账之前,首先要获得这两个Account对象的锁,以确保通过原子方式来更新两个账户中的余额,看上去没什么毛病,但是如果有两个线程同时调用transferMoney,其中一个线程从X向Y转账,另一个线程从Y向X转账,那么就可能发生死锁。

//A线程:transferMoney(myAccount,yourAccount,10);
//B线程:transferMoney(yourAccount,myAccount,20);
public void transferMoney(Account fromAccount,
Account toAccount,
Amount amount)
throws InsufficientFundsException{
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance().compareTo(account) <0) {//账户余额不能为0
throw new InsufficientFundsException();
}
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}

前面提到过,如果能让线程访问锁的顺序一致,可以避免锁顺序死锁,可以通过两个账户的hashCode判断,修改代码:

private static final Object tieLock = new Object();

public void transferMoney(Account fromAccount,
Account toAccount,
Amount amount)
throws InsufficientFundsException{
class Helper{
public void transfer() throws InsufficientFundsException{
if (fromAccount.getBalance().compareTo(account) <0) {//账户余额不能为0
throw new InsufficientFundsException();
}
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}

int fromHash = System.identityHashCode(fromAccount);
int toHash = System.identityHashCode(toAccount);

if (fromHash < toHash) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().transfer();
}
}
}
else if (fromHash > toHash) {
synchronized (toAccount) {
synchronized (fromAccount) {
new Helper().transfer();
}
}
}
else {
synchronized (tieLock) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().transfer();
}
}
}
}
}

  这样就避免了上面提到的两个账户互相转账的问题,两个对象的hashCode如果相同,则又添加了一个锁,保证每次只有一个线程以未知顺序访问锁。你可能会问,为什么不直接在最外面再加一层锁呢,把hashCode的判断直接省去,肯定不能这样做,如果这样,那么转账就成了串行的了,这样毫无并发可言,好在System.identityHashCode发生哈希码相同的时候非常少。增加了代码,至少解决了问题,这样,这个方法至少能用了。

  在大型网站中每天可能要执行数十亿次获取锁和释放锁的操作,只要有一次发生错误产生死锁,程序都会蹦掉,而且,有时候应用程序即使通过了压力测试也不可能找到所有潜在的死锁。很多潜在的死锁比上面的例子更加隐晦,这里就不写例子了,那么有什么最好的解决办法让我们尽量的避免锁顺序死锁呢,答案是在程序中始终使用开放调用

  什么是开放调用,我自己的理解是,在方法中调用某个外部方法时,没有持有任何锁,也就是说,锁用完了,赶紧释放掉。这也就是为什么不提倡直接用synchronized直接修饰方法的最重要的原因。我想获得哪个锁,我获得,用完了,赶紧释放掉,哪怕过几行代码我还要用到这个锁,我也不会多占用这个锁一秒钟。这个只能说是一个良好的编程习惯,但它可以尽量规避锁顺序死锁。

 二、性能与可伸缩性

  1、系统为什么要做成分布式

  程序的性能由多个指标来衡量,比如服务时间、延迟时间、吞吐率、效率、可伸缩性以及容量等。服务时间和等待时间指某个任务需要多快才能完成,吞吐量指在计算资源一定的情况下,能完成多少工作。可伸缩性指的是:当增加计算资源时(例如CPU、内存、存储容量或I/O带宽),程序的吞吐量或者处理能力相应地增加。

  性能的多快和多少是完全独立的,有时是互相矛盾的,我们熟悉的mvc三层结构是彼此独立的,并且可能由不同系统处理,这个例子很好的说明了提高可伸缩性通常会造成性能损失的原因,如果三层在单个系统中,处理第一个请求的时候其性能肯定高于将程序分成多层并将不同层次分布到多个系统时的性能。(LZ在面试时曾被问到具体说说是哪里带来了延迟,LZ当时只回答是网络通信,系统调用、数据复制之间的延迟,结果面试官很不满意,一再追问,大家可以自行百度,当做一个面试题记下来,预防面试官为难。)然而,当这种单一系统到达自身处理能力的极限时,会遇到一个很严重的问题:要进一步处理大量请求会非常难,因此,通常会接受每个请求执行更长时间或者消耗更多资源来换取更高的负载,这也就是为什么系统分布式的原因。

  2、线程带来的问题

  (1)上下文切换

  如果可运行的线程数大于CPU数量,那么OS会将某个正在运行的线程调度出来,从而使其他线程能够使用CPU,这将导致一次上下文切换,也就带来一些开销。

  (2)锁竞争

  当线程由于等待某个锁而被阻塞时,JVM通常会将这个线程挂起,并允许它被交换出去,这也可能导致上下文切换。

  3、减少锁竞争

  通过上面介绍,串行操作降低可伸缩性,上下文切换降低性能,锁竞争会同时导致这两个问题,因此减少锁竞争能提高性能和可伸缩性。在并发程序中,对可伸缩性最主要威胁就是独占方式的资源锁。那怎么做呢,上面提到过了,开放调用,用完锁,赶紧释放,还有就是减少锁的粒度:锁分解和锁分段。这块内容LZ打算单独写一份笔记。

  这块内容理论的东西很多,很多东西很难展开介绍,先看下这几个点吧,其实所有的知识点都指向如何避免锁竞争这个事情上,开放调用是一个办法,但是作用没有锁分段大。LZ也是刚开始用博客记录一些新知识,可能很多时候写东西写不到重点,我自己也尽量避免这个问题,慢慢来吧,很羡慕面对新知识能一下子抓住重点的大神,LZ只是一个想往上再走一步的菜鸟,各位多提意见,感谢大家。


推荐阅读
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 像跟踪分布式服务调用那样跟踪Go函数调用链 | Gopher Daily (2020.12.07) ʕ◔ϖ◔ʔ
    每日一谚:“Acacheisjustamemoryleakyouhaven’tmetyet.”—Mr.RogersGo技术专栏“改善Go语⾔编程质量的50个有效实践” ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 怀疑是每次都在新建文件,具体代码如下 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
author-avatar
冷漠自逍遥2602897565
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有