模块化的乐趣
模块间的紧耦合是一种很糟糕的设计,而耦合的最坏表现就是模块间的循环依赖。幸运的是,有几种方法可以用来消除循环依赖,分别是回调函数,代码上移,代码下移。 接下来,我会为大家展示一个小例子。示例中,我会分别使用上述几种技术来消除循环依赖。 在消除循环依赖之后,我们会探索另外两项技术,来达到依赖反转和消除模块之间依赖的目的。本示例的所有代
Java内存模型FAQ(十一)新的内存模型是否修复了双重锁检查问题?
臭名昭著的双重锁检查(也叫多线程单例模式)是一个骗人的把戏,它用来支持lazy初始化,同时避免过度使用同步。在非常早的JVM中,同步非常慢,开发人员非常希望删掉它。双重锁检查代码如下:// double-checked-locking - don't do this!private static Something instance = null;public Something getI
Java 8 特性 – 终极手册
1.简介毫无疑问,Java 8是自Java 5(2004年)发布以来Java语言最大的一次版本升级,Java 8带来了很多的新特性,比如编译器、类库、开发工具和JVM(Java虚拟机)。在这篇教程中我们将会学习这些新特性,并通过真实例子演示说明它们适用的场景。本教程由下面几部分组成,它们分别涉及到Java平台某一特定方面的内容:语言编译器类库开发工具运行时(Java虚拟机)2.Java
Java内存模型FAQ(九)在新的Java内存模型中,final字段是如何工作的
一个对象的final字段值是在它的构造方法里面设置的。假设对象被正确的构造了,一旦对象被构造,在构造方法里面设置给final字段的的值在没有同步的情况下对所有其他的线程都会可见。另外,引用这些final字段的对象或数组都将会看到final字段的最新值。对一个对象来说,被正确的构造是什么意思呢?简单来说,它意味着这个正在构造的对象的引用在构造期间没有被允许逸出。(参见安全构造技术)。换句话说,不要让
Java内存模型FAQ(八)Final字段如何改变它们的值
我们可以通过分析String类的实现具体细节来展示一个final变量是如何可以改变的。String对象包含了三个字段:一个character数组,一个数组的offset和一个length。实现String类的基本原理为:它不仅仅拥有character数组,而且为了避免多余的对象分配和拷贝,多个String和StringBuffer对象都会共享相同的character数组。因此,String.sub
Java内存模型FAQ(七)同步会干些什么呢
同步有几个方面的作用。最广为人知的就是互斥 ——一次只有一个线程能够获得一个监视器,因此,在一个监视器上面同步意味着一旦一个线程进入到监视器保护的同步块中,其他的线程都不能进入到同一个监视器保护的块中间,除非第一个线程退出了同步块。但是同步的含义比互斥更广。同步保证了一个线程在同步块之前或者在同步块中的一个内存写入操作以可预知的方式对其他有相同监视器的线程可见。当我们退出了同步块,我们就
Java内存模型FAQ(六)没有正确同步的含义是什么?
没有正确同步的代码对于不同的人来说可能会有不同的理解。在Java内存模型这个语义环境下,我们谈到“没有正确同步”,我们的意思是:一个线程中有一个对变量的写操作,另外一个线程对同一个变量有读操作,而且写操作和读操作没有通过同步来保证顺序。当这些规则被违反的时候,我们就说在这个变量上有一个“数据竞争”(data race)。一个有数据竞争的程序就是一个没有正确同步的程序。https://shimo.i
Java内存模型FAQ(五)旧的内存模型有什么问题?
旧的内存模型中有几个严重的问题。这些问题很难理解,因此被广泛的违背。例如,旧的存储模型在许多情况下,不允许JVM发生各种重排序行为。旧的内存模型中让人产生困惑的因素造就了JSR-133规范的诞生。例如,一个被广泛认可的概念就是,如果使用final字段,那么就没有必要在多个线程中使用同步来保证其他线程能够看到这个字段的值。尽管这是一个合理的假设和明显的行为,也是我们所期待的结果。实际上,在旧的内存模
Java内存模型FAQ(十)volatile是干什么用的
Volatile字段是用于线程间通讯的特殊字段。每次读volatile字段都会看到其它线程写入该字段的最新值;实际上,程序员之所以要定义volatile字段是因为在某些情况下由于缓存和重排序所看到的陈旧的变量值是不可接受的。编译器和运行时禁止在寄存器里面分配它们。它们还必须保证,在它们写好之后,它们被从缓冲区刷新到主存中,因此,它们立即能够对其他线程可见。相同地,在读取一个volatile字段之前
Java内存模型FAQ(四)重排序意味着什么?
在很多情况下,访问一个程序变量(对象实例字段,类静态字段和数组元素)可能会使用不同的顺序执行,而不是程序语义所指定的顺序执行。编译器能够自由的以优化的名义去改变指令顺序。在特定的环境下,处理器可能会次序颠倒的执行指令。数据可能在寄存器,处理器缓冲区和主内存中以不同的次序移动,而不是按照程序指定的顺序。例如,如果一个线程写入值到字段a,然后写入值到字段b,而且b的值不依赖于a的值,那么,处理器就能够
Java内存模型FAQ(三)JSR133是什么?
从1997年以来,人们不断发现Java语言规范的17章定义的Java内存模型中的一些严重的缺陷。这些缺陷会导致一些使人迷惑的行为(例如final字段会被观察到值的改变)和破坏编译器常见的优化能力。Java内存模型是一个雄心勃勃的计划,它是编程语言规范第一次尝试合并一个能够在各种处理器架构中为并发提供一致语义的内存模型。不过,定义一个既一致又直观的内存模型远比想象要更难。JSR133为Java语言定
Java内存模型FAQ(二) 其他语言,像C++,也有内存模型吗?
大部分其他的语言,像C和C++,都没有被设计成直接支持多线程。这些语言对于发生在编译器和处理器平台架构的重排序行为的保护机制会严重的依赖于程序中所使用的线程库(例如pthreads),编译器,以及代码所运行的平台所提供的保障。https://shimo.im/docs/3wGJrJGJgRppK8CT/ 《Java内存模型FAQ(一) 什么是内存模型》原文Do other languages, l
Java内存模型FAQ(一) 什么是内存模型
在多核系统中,处理器一般有一层或者多层的缓存,这些的缓存通过加速数据访问(因为数据距离处理器更近)和降低共享内存在总线上的通讯(因为本地缓存能够满足许多内存操作)来提高CPU性能。缓存能够大大提升性能,但是它们也带来了许多挑战。例如,当两个CPU同时检查相同的内存地址时会发生什么?在什么样的条件下它们会看到相同的值?在处理器层面上,内存模型定义了一个充要条件,“让当前的处理器可以看到其他处理器写入
从Java视角理解系统结构(二)CPU缓存
众所周知, CPU是计算机的大脑, 它负责执行程序的指令; 内存负责存数据, 包括程序自身数据. 同样大家都知道, 内存比CPU慢很多. 其实在30年前, CPU的频率和内存总线的频率在同一个级别, 访问内存只比访问CPU寄存器慢一点儿. 由于内存的发展都到技术及成本的限制, 现在获取内存中的一条数据大概需要200多个CPU周期(CPU cycles), 而CPU寄存器一般情况下1个CPU周期就够
从Java视角理解系统结构(一)CPU上下文切换
在高性能编程时,经常接触到多线程. 起初我们的理解是, 多个线程并行地执行总比单个线程要快, 就像多个人一起干活总比一个人干要快. 然而实际情况是, 多线程之间需要竞争IO设备, 或者竞争锁资源,导致往往执行速度还不如单个线程. 在这里有一个经常提及的概念就是: 上下文切换(Context Switch).上下文切换的精确定义可以参考: http://www.linfo.org/cont
Java 锁机制了解一下
在多线程环境下,程序往往会出现一些线程安全问题,为此,Java提供了一些线程的同步机制来解决安全问题,比如:synchronized锁和Lock锁都能解决线程安全问题。悲观锁和乐观锁我们可以将锁大体分为两类:悲观锁乐观锁顾名思义,悲观锁总是假设最坏的情况,每次获取数据的时候都认为别的线程会修改,所以每次在拿数据的时候都会上锁,这样其它线程想要修改这个数据的时候都会被阻塞直到获取锁。比如MySQL数
IDEA 这个小技巧太实用了!随时随用。。。
当你在使用IDEA编写项目代码的时候,是否遇到过如下一些情况?与别人沟通,需要简写某个功能的伪代码想记录一些json格式的数据,并且能格式化,即JSON文档功能想记录一些其他语言例如HTML,CSS,SQL,XML等的代码突然有另外一个项目的思路,想记录下代码,以备切换另外一个项目时能用到临时记录HTTP Client调用记录…当遇到以上情况,正常可能要打开其他软件或编辑器来进行操作,不同软件间切
这九个编程好习惯,新手程序员秒变老司机
之前在知乎上回答了这个问题,最近有一些读者来咨询,说刚工作,有什么需要注意的编程好习惯。结合自己工作一年多的经(cai)验(keng),给大家分享下在编程中,有哪些好的习惯应该从一开始就坚持。一、先想通逻辑,再写代码有一种斐波那契程序员:每天都在修复昨天和前天的 bug相信很多开发的同学,都有过这样的经历:代码删了又写,写了又删。在敲代码之前,其实可以先问自己一个问题:我写下的这行代码
Java List的remove()方法陷阱
Java的List在删除元素时,一般会用list.remove(o)/remove(i)方法。在使用时,容易触碰陷阱,得到意想不到的结果。总结以往经验,记录下来与大家分享。 首先初始化List,代码如下:package com.cicc.am.test; import java.util.ArrayList;import java.util.List;public class List
面试必问:信号量与生产者消费者问题!
生产者—消费者问题生产者—消费者题型在各类考试(考研、程序员证书、程序员面试笔试、期末考试)很常见,原因之一是生产者—消费者题型在实际的并发程序(多进程、多线程)设计中很常见;之二是这种题型综合性较好,涉及进程合作、互斥,有时还涉及死锁的避免。生产者—消费者题型可以全面考核你对并发程序的理解和设计能力。生产者—消费者题型最基本的是有界缓冲区的生产者消费者问题和无界缓冲区的生产者消费者问题,对这两个