2024-12-26 21:01:19 | 来源: 互联网整理
Beyond Compare 是一款不可多得的专业级的文件夹和文件对比工具。使用他可以很方便的对比出两个文件夹或者文件的不同之处,并把相差的每一个字节用颜色加以表示,查看方便,并且支持多种规则对比。对程序编辑、修改者来说,Beyond Compare绝对是一款不可多得的工具。
首先我们来认识软件的界面
软件的界面布局很饱满,没有大起大落的色彩搭配,给人的感觉很平和所示。命令菜单和工作区呈上下的排列,由于是两个项目间的比较,因此,在工作区上分为了两个部分,分别对应着参与比较的项目。每次的比较操作,都被看作是一次“会话”,因此,通过“会话”菜单来建立比较任务。
Beyond Compare 4中文版下载地址可参考:http://www.beyondcompare.cc/xiazai.html
体验一下富有人性化的强大功能
(1)比较的对象包罗万象
参与比较的对象类型非常的丰富,如所有类型的文件、文件夹、压缩包、FTP网站等,总之有了它,在浩瀚的资料面前不用再眉头紧皱,遥遥无期的感觉被它一扫而光。另外,谈到比较自然涉及到依据的标准是什么?
可以通过“会话”菜单中的“会话设置”来设定“常规”和“高级”选项。也可以根据不同的“会话”来定义一系列的“名称过滤”、“时间过滤”、“其他过滤”、“比较标准”、“其他标准”等特定选项,并由“会话管理”命令来进行管理,为下次同类型的比较做一劳永逸的准备,多么的人性化啊!
(2)建立比较任务的方式各有不同
比较两个对象,可以通过打开程序后,建立所示的“会话向导”方式来分别指定它们;也可以通过右键功能菜单来达到。比如要比较两个文件夹的差异,可以右键点击文件夹1,在上下功能菜单中选择“选为左侧对比窗口”命令,然后,再找到另一个文件夹2,点击右键选择“与文件夹1比较”命令,于是便建立了比较任务。
从随机性上看,觉得后者是比较灵活方便的,用前一种方式,显得比较罗嗦些,要启动程序,然后再启动向导。但是,从要对比较的对象进行操作的角度来讲,用“会话向导”则显得目的性强些。
(3)自定义查看方式
对比结果显示出来后,我们可以根据自己要查询的内容,有选择的查看结果。通过点击“视图”菜单,可以对查看进行过滤,比如只看差异的部分或者匹配的部分等,方式很多,便于迅速的在结果中定位我们要找的东西。
这里有一个技巧,就是要熟知配置中设定的“差异文件”、“匹配文件”、“孤立文件”、“较新文件”的图示标记,具体的看一下“查看”菜单中的“图例”命令便可了解。
Beyond Compare软件做的很细致,平时在资源管理器中涉及到的文件、文件夹的操作在它上面都被成功体现,一方面它实现比较功能,另一方面与资源管理器紧密结合,在对比中完成对文件或文件夹的管理,体现了完美的兼容性和高效性!
它的人性化的前期准备,比如差异显示、匹配显示、孤立文件的提取、较新文件的提取等方式,也是一大特色,为后期的文件及文件夹的处理奠定了基础。
https://blog.biezhi.me/2019/01/head-first-cas.html
我们一想到在多线程下保证安全的方式头一个要拎出来的肯定是锁,不管从硬件、操作系统层面都或多或少在使用锁。锁有什么缺点吗?当然有了,不然 JDK 里为什么出现那么多各式各样的锁,就是因为每一种锁都有其优劣势。
使用锁就需要获得锁、释放锁,CPU 需要通过上下文切换和调度管理来进行这个操作,对于一个 独占锁 而言一个线程在持有锁后没执行结束其他的哥们就必须在外面等着,等到前面的哥们执行完毕 CPU 大哥就会把锁拿出来其他的线程来抢了(非公平)。锁的这种概念基于一种悲观机制,它总是认为数据会被修改,所以你在操作一部分代码块之前先加一把锁,操作完毕后再释放,这样就安全了。其实在 JDK1.5 使用 synchronized 就可以做到。
但是像上面的操作在多线程下会让 CPU 不断的切换,非常消耗资源,我们知道可以使用具体的某一类锁来避免部分问题。那除了锁的方式还有其他的吗?当然,有人就提出了无锁算法,比较有名的就是我们今天要说的 CAS(compare and swap),和锁不同的是它是一种乐观的机制,它认为别人去拿数据的时候不会修改,但是在修改数据的时候去判断一下数据此时的状态,这样的话 CPU 不会切换,在读多的情况下性能将得到大幅提升。当前我们使用的大部分 CPU 都有 CAS 指令了,从硬件层面支持无锁,这样开发的时候去调用就可以了。
不论是锁还是无锁都有其优劣势,后面我们也会通过例子说明 CAS 的问题。
前面提了无锁的 CAS,那到底 CAS 是个啥呢?我已经迫不及待了,我们来看看维基百科的解释
比较并交换(compare and swap, CAS),是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值。
CAS 给我们提供了一种思路,通过 比较 和 替换 来完成原子性,来看一段代码:
这是一段 c 语言代码,可以看到有 3 个参数,分别是:
只要我们当前传入的进行比较的值和内存里的值相等,就将新值修改成功,否则返回 0 告诉比较失败了。学过数据库的同学都知道悲观锁和乐观锁,乐观锁总是认为数据不会被修改。基于这种假设 CAS 的操作也认为内存里的值和当前值是相等的,所以操作总是能成功,我们可以不需要加锁就实现多线程下的原子性操作。
在多线程情况下使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被阻塞挂起,而是告诉它这次修改失败了,你可以重新尝试,于是可以写这样的代码。
不过这样的代码相信你可能看出其中的蹊跷了,这个我们后面来分析,下面来看看 Java 里是怎么用 CAS 的。
还是前面的问题,如果让你用 Java 的 API 来实现你可能会想到两种方式,一种是加锁(可能是 synchronized 或者其他种类的锁),另一种是使用 atomic 类,如 AtomicInteger,这一系列类是在 JDK1.5 的时候出现的,在我们常用的 java.util.concurrent.atomic 包下,我们来看个例子:
这个例子开启了 5000 个线程去进行累加操作,不管你执行多少次答案都是 5000。这么神奇的操作是如何实现的呢?就是依靠 CAS 这种技术来完成的,我们揭开 AtomicInteger 的老底看看它的代码:
这里我只帖出了我们前面例子相关的代码,其他都是类似的,可以看到 incrementAndGet 调用了 unsafe.getAndAddInt 方法。Unsafe 这个类是 JDK 提供的一个比较底层的类,它不让我们程序员直接使用,主要是怕操作不当把机器玩坏了。。。(其实可以通过反射的方式获取到这个类的实例)你会在 JDK 源码的很多地方看到这家伙,我们先说说它有什么能力:
这里只是大致提一下常用的操作,具体细节可以在文末的参考链接中查看。下面我们继续看 unsafe 的 getAndAddInt 在做什么。
其实很简单,先通过 getIntVolatile 获取到内存的当前值,然后进行比较,展开 compareAndSwapInt 方法的几个参数:
所以 this.compareAndSwapInt(var1, var2, var5, var5 + var4) 的意思就是,比较一下 var2 和内存当前值 var5 是否相等,如果相等那我就将内存值 var5 修改为 var5 + var4(var4 就是 1,也可以是其他数)。
这里我们还需要解释一下 偏移量 是个啥?你在前面的代码中可能看到这么一段:
可以看出在静态代码块执行的时候将 AtomicInteger 类的 value 这个字段的偏移量获取出来,拿这个 long 数据干嘛呢?在 Unsafe 类里很多地方都需要传入 obj 和偏移量,结合我们说 Unsafe 的诸多能力,其实就是直接通过更底层的方式将对象字段在内存的数据修改掉。
使用上面的方式就可以很好的解决多线程下的原子性和可见性问题。由于代码里使用了 do while 这种循环结构,所以 CPU 不会被挂起,比较失败后重试,就不存在上下文切换了,实现了无锁并发编程。
你留意上面的代码会发现一个问题,while 循环如果在最坏情况下总是失败怎么办?会导致 CPU 在不断处理。像这种 while(!compareAndSwapInt) 的操作我们称之为自旋,CAS 是乐观的,认为大家来并不都是修改数据的,现实可能出现非常多的线程过来都要修改这个数据,此时随着并发量的增加会导致 CAS 操作长时间不成功,CPU 也会有很大的开销。所以我们要清楚,如果是读多写少的情况也就满足乐观,性能是非常好的。
提到 CAS 不得不说 ABA 问题,它是说假如内存的值原来是 A,被一个线程修改为了 B,此时又有一个线程把它修改为了 A,那么 CAS 肯定是操作成功的。真的这样做的话代码可能就有 bug 了,对于修改数据为 B 的那个线程它应该读取到 B 而不是 A,如果你做过数据库相关的乐观锁机制可能会想到我们在比较的时候使用一个版本号 version 来进行判断就可以搞定。在 JDK 里提供了一个 AtomicStampedReference 类来解决这个问题,来看一个例子:
它的构造函数是 2 个参数,多传入了一个初始 时间戳,用这个戳来给数据加了一个版本,这样的话多个线程来修改如果提供的戳不同。在修改数据的时候除了提供一个新的值之外还要提供一个新的戳,这样在多线程情况下只要数据被修改了那么戳一定会发生改变,另一个线程拿到的是旧的戳所以会修改失败。
既然 CAS 提供了这么好的 API,我们不妨用它来实现一个简易版的独占锁。思路是当某个线程进入 lock 方法就比较锁对象的内存值是否是 false,如果是则代表这把锁它可以获取,获取后将内存之修改为 true,获取不到就自旋。在 unlock 的时候将内存值再修改为 false 即可,代码如下:
这里使用了 AtomicBoolean 这个类,当然用 AtomicInteger 也是可以的,因为我们只保存一个状态 boolean 占用比较小就用它了。这个锁的实现比较简单,缺点非常明显,由于 while 循环导致的自旋会让其他线程都在占用 CPU,但是也可以使用,关于锁的优化版本实现我会在后续的文章中进行改进和说明,正因为这些问题我们也会在后续研究 AQS 这把利器的优点。
看了上面的这些代码和解释相信你对 CAS 已经理解了,下面我们要说的原理是前面的 native 方法中的 C++ 代码写了什么,在 openjdk 的 /hotspot/src/share/vm/prims 目录中有一个 Unsafe.cpp 文件中有这样一段代码:
注意:这里以 hotspot 实现为例
热门手游排行榜
用户评论
对比什么啊,你得说清楚了!
有10位网友表示赞同!
想了解两个东西之间的差异?
有20位网友表示赞同!
这篇文章要分析哪两方啊?
有10位网友表示赞同!
比较是很有用的工具啊,可以帮助我们更好地理解。
有8位网友表示赞同!
什么样的对比会更有趣呢?
有18位网友表示赞同!
我最近也对一些东西进行对比研究呢。
有15位网友表示赞同!
这种类型的文章很能让我思考东西的真谛。
有9位网友表示赞同!
期待看到作者的比较结果!
有17位网友表示赞同!
对比分析可以帮助我们做出明智的选择啊。
有8位网友表示赞同!
这篇文章肯定有用!
有7位网友表示赞同!
我很喜欢阅读这类分析类的文章呢。
有15位网友表示赞同!
希望文章能够深入浅出地讲解。
有5位网友表示赞同!
这种比较有很强的实用性啊。
有5位网友表示赞同!
我对这个主题很有兴趣,期待阅读!
有14位网友表示赞同!
我觉得对比需要考虑很多因素才能更准确。
有6位网友表示赞同!
这篇文章会帮我找到一些新的思路吗?
有14位网友表示赞同!
作者的比较标准是什么呢?
有8位网友表示赞同!
对比分析可以帮助我们发现问题和差距哦。
有18位网友表示赞同!
希望看到一些数据支持作者的观点!
有19位网友表示赞同!