江疏影读书系列之Java并发编程实战

  欲速则不达,欲达则欲速!
  
  12年毕业,化工、零售。16年转行,Java培训五个月,17年1月,我人生中最悲惨的一个月,找工作处处碰壁,啥也不会,欲哭无泪...
  
  转眼间,以程序员的身份工作两年半了,一路走来,跌跌拌拌,漫天的嘘声,都无所谓,依旧挡不住我前进的步伐。
  
  本系列是《Java并发编程实战》的读书笔记,方便自己查阅。
  
  第一章 简介
  
  一、为什么要使用线程编写并发程序?
  
  1、它们能使复杂的异步代码变得更简单,从而极大地简化了复杂系统的开发。
  
  2、充分发挥多核处理器的强大计算能力。
  
  二、并发简史
  
  1、计算机中加入操作系统来实现同时执行多个程序,并且不同的程序都在单独的进程中运行:操作系统为各个独立的进程分配各种资源,包括内存,文件句柄以及安全证书等。如果需要的话,在不同进程之间可以通过一些粗粒度的通信机制来交换数据,包括套接字、信号处理器、共享内存、信号量以及文件。
  
  ① 资源利用率,等待IO的同时可以运行另一个程序
  
  ② 公平性,时间分片同等的使用和共享计算机资源
  
  ③ 便利性
  
  2、线程的优势
  
  如果使用得当,线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能,线程能够将大部分的异步工作流转换成串行工作流。
  
  在GUI(图形用户界面)应用程序中,线程可以提高用户界面的响应灵敏度,而在服务器应用程序中,可以提升资源利用率以及系统吞吐率。线程还简化JVM的实现,垃圾回收机制通常在一个或多个线程中运行。
  
  var tab = new Vue({
  
  el: '#tab',
  
  data: {
  
  tabNumber: 1, //标签数量,这个是临时的,便于自动重新绑定
  
  currentTabId: www.yfylhl.com, //当前激活的tab的id
  
  beforeTabId: 1, //上一个被激活的tab的id
  
  tabs: {
  
  tab1: { //可以有多个标签,这里先默认一个tab
  
  id: "1", //标签识别标示
  
  title: "我的桌面",
  
  isShow:true, //是否显示
  
  message: '桌面',
  
  orderBy: [], //可以控制字段的先后顺序,想调整列的先后顺序,改这个数组就行,可以做个性化设置
  
  tableTh: {}, //表头的描述信息
  
  dataList: [] //数据包,字段名作为关键字,便于列的调整先后顺序
  
  }
  
  }
  
  },
  
  methods: {
  
  tabClick: function (www.haojiangyule.com) {
  
  //切换tab
  
  //alert("切换tab" + id);
  
  //隐藏当前的tab
  
  var oldId = tab.currentTabId; //记录切换前tab的id
  
  tab.beforeTabId = oldId;
  
  tab.tabs["tab" + oldId].isShow = false; //隐藏切换前的tab
  
  tab.currentTabId = id; //记录切换后的id
  
  tab.tabs["tab" + id].isShow = true; //显示切换后的tab
  
  },
  
  closeTab: function (www.leyouzaixan.cn) {
  
  if (id === "1") {
  
  alert("这是桌面,建议不要关闭哦:)");
  
  return;
  
  }
  
  delete tab.tabs["tab" + id]; //这种方式不会被vue监控到,所以不会触发视图的刷新
  
  tab.tabNumber = tab.tabNumber - 1; //这样子凑合一下。触发视图的刷新
  
  //设置“激活”状态
  
  var oldId = tab.beforeTabId;www.yfykLe2019.com //上一个激活tab
  
  var nowId = tab.currentTabId; //现在激活tab
  
  if (nowId === id) {
  
  //关掉的是激活tab,需要设置上一个tab为激活状态
  
  tab.currentTabId = oldId;
  
  tab.tabs["tab" + oldId].isShow = true; //这么写好像可以触发视图的刷新
  
  tab.beforeTabId = 1;
  
  }
  
  else if (oldId www.zbzxyL12.com=== id) {
  
  //关闭的是上一个激活tab,修改前一个tab的id
  
  tab.beforeTabId = 1;
  
  }else {
  
  //需要强制修改一下,否则不会刷新,emmm,好吧还是没自动刷新
  
  tab.currentTabId = nowId;
  
  }
  
  }
  
  }
  
  });
  
  复制代码
  
  methods 这个也是一个保留字(关键字),就是放方法的。里面可以有多个方法,方法名称和标签里的v-on:XXXX=’00000’ 对应。
  
  这里写了两个方法,一个是切换tab的,一个是
  
  4、线程带来的风险
  
  ① 安全性问题
  
  由于多个线程要共享相同的内存地址空间,并且并发运行,因此它们可能会访问或修改其它线程正在使用的变量。
  
  ② 活跃性问题
  
  死锁、饥饿、活锁等
  
  ③ 性能问题
  
  性能问题主要包括服务时间过长、响应不灵敏、吞吐率过低、资源消耗过高、可伸缩性较低等。
  
  第二章 线程安全性
  
  要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问。
  
  正确的编程方法:首先使代码正确运行,然后再提高代码的速度。即使如此,最好也只是当性能测试结果和应用需求告诉你必须提高性能,以及测量结果表明这种优化在实际环境中确实能带来性能提升时,才进行优化。
  
  1、原子性
  
  ① count操作包括读取-修改-写入的操作序列,并且其结果状态依赖于之前的状态。
  
  竞态条件(Race Condition):不恰当的执行时序而出现不正确的结果。
  
  ② 先检查后执行:即通过一个可能失效的观测结果来决定下一步的动作。延迟初始化中的竞态条件
  
  假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说是原子的。原子操作是指,对于一个状态的所有操作(包括该操作本身)来说,这个操作是一个以原子方式执行的操作。
  
  在java.util.concurrent.atomin包中包含了一些原子变量类,用于实现在数值和对象引用上的原子状态转换。当状态变量的数据由一个变为多个时,并不会像状态变量数量由零个变为一个那样简单。
  
  2、加锁机制
  
  要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量。
  
  内置锁:同步代码块
  
  同步代码块包括两个部分:一是作为锁的对象引用,一个是作为由个锁保护的代码块。以关键字Synchronized来修饰的方法就是一种横跨整个方法体的同步代码块,其中该同步代码块的锁就是方法调用所在的对象。静态方法以Class对象作为锁。
  
  每个Java对象都可以用做一个实现同步的锁,称为内置锁(Instrinsic Lock)或监视器锁(Monitor Lock)。线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时自动释放锁。Java的内置锁相当于一种互斥体,每次只有一个线程执行内置锁保护的代码块,因此这个锁保护的同步代码会以原子方式执行。
  
  缺点:并发性有点差。
  
  重入:如果某个线程试图获取一个已经由它自己持有的锁,那么这个请求就会成功。”重入”意味着获取锁的操作的粒度是”线程”,而不是”调用”。
  
  重入的一种实现方法是,为每个锁关联一个获取计数值和一个所有都线程。
  
  子类改写父类的synchronized方法,然后调用父类中的方法,此时如果没有可重入的锁,那么这段代码将会产生死锁。
  
  3、用锁来保护状态
  
  对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有一个锁,在这种情况下,我们称状态是由个锁保护的。
  
  每个共享的和可变的变量都应该只由一个锁来保护,从而使维护人员知道是哪一个锁。
  
  对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护。
  
  多个操作合并为一个复合操作,还是需要额外的加锁机制。每个方法都作为同步方法还可能导致活跃性问题和性能问题。
  
  4、活跃性与性能
  
  可同时调用的数量,不仅受到可用处理资源的限制,还受到应用程序本身结构的限制。
  
  同时代码块中同时更新变量,并且在所有访问它们的位置上都使用同步。位于同步代码块之外的代码以独占的方式来访问局部(位于栈上的)变量,这些变量不会在多个线程间共享,因此不需要同步。
  
  已经使用了同步代码块来构造原子操作,而使用两种不同的同步机制不仅会带来混乱,也不会在性能或安全性上带来任何好处,因此在这里不使用原子变量。
  
  同步代码块的合理大小,要在安全性、简单性和性能之间做权衡。
  
  通常,在简单性与性能之间存在着相互制约因素。当实现某个同步策略时,一定不要盲目地为了性能而牺牲简单性。
  
  当执行时间较长的计算或者可能无法快速完成的操作时(例如,网络I/O或控制台I/O),一定不要持有锁。

转载于:https://www.cnblogs.com/qwangxiao/p/11213764.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值