0%

Atomic这个包主要是一个小型工具包,支持单个变量上的无锁线程安全编程。

主要的类如下图

upload successful

其中other这一类是java 8中新增加的类,后面会介绍。

虽然这包下面类很多,但是你只需要看懂其中一个,其余的方法和使用都是大同小异。

本篇文章主要内容如下

  1. CAS 介绍
  2. 基本类型解析以及使用
  3. 引用类型解析以及使用
  4. 数组类型解析以及使用
  5. 属性原子修改器(Updater)解析以及使用
  6. Java 8新增加的LongAdder等类的解析以及使用
阅读全文 »

上一篇文章已经分析了公平锁的获取与释放,本篇文章在前文的基础上分析非公平锁的获取与释放。如果你看懂跑了前面公平锁的获取与释放主要流程,那么看懂本篇文章将会比较轻松。

获取非公平锁

非公平锁和公平锁在获取锁的方法上,流程是一样的;它们的区别主要表现在“尝试获取锁的机制不同。简单点说,公平锁在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待);而非公平锁在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁)。
在前面的java线程系列 JUC锁 03 ReentrantLock公平锁”中,已经详细介绍了获取公平锁的流程和机制;下面,通过代码分析以下获取非公平锁的流程。

阅读全文 »

基本概念

本文讲解线程获取和释放公平锁的原理;在讲解之前,需要了解几个基本概念。后面的内容,都是基于这些概念的;这些概念可能比较枯燥,但从这些概念中,能窥见java锁的一些架构,这对我们了解锁是有帮助的。同时最好看下前面一篇文章:aqs源码分析

AQS – 指AbstractQueuedSynchronizer类。

AQS是JUC中所有锁内部具体实现依赖的的抽象类,锁的许多公共方法都是在这个类中实现。AQS是独占锁(例如,ReentrantLock)和共享锁(例如,Semaphore)的公共父类。

AQS需要下面三个基本组件的相互协作:

  • 同步状态的原子性管理;
  • 线程的阻塞与解除阻塞;
  • 队列的管理;

创建一个框架分别实现这三个组件是有可能的。但是,这会让整个框架既难用又没效率。例如:存储在队列节点的信息必须与解除阻塞所需要的信息一致,而暴露出的方法的签名必须依赖于同步状态的特性。

同步器框架的核心决策是为这三个组件选择一个具体实现,同时在使用方式上又有大量选项可用。这里有意地限制了其适用范围,但是提供了足够的效率,使得实际上没有理由在合适的情况下不用这个框架而去重新建造一个。

阅读全文 »

前文已经对AbstractQueuedSynchronizer做了详细的介绍,本篇文章主要从实现的角度来看。

AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。当然,我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器。

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

核心是对下面三个进行实现

  • 同步状态的原子性管理;
  • 线程的阻塞与解除阻塞;
  • 队列的管理;
阅读全文 »

摘要

在J2SE 1.5的java.util.concurrent包(下称j.u.c包)中,大部分的同步器(例如锁,屏障等等)都是基于AbstractQueuedSynchronizer(下称AQS类)这个简单的框架来构建的。这个框架为同步状态的原子性管理、线程的阻塞和解除阻塞以及排队提供了一种通用机制。这篇论文主要描述了这个框架基本原理、设计、实现、用法以及性能。

阅读全文 »

内存分配

Java技术体系的自动内存管理,最根本的目标是自动化地解决两个问题:自动给对象分配内存以 及自动回收分配给对象的内存。关于回收内存这方面,在前文的垃圾回收相关文章中已经介绍。本篇文章探讨一下关于给对象分配内存的那些事儿。

这里主要介绍JVM通用的分配规则,不通的垃圾回收器可能会有写差别,但是大体都是相似的。

对象优先在Eden分配

大多数情况下,对象在新生代Eden区进行分配。当Eden区没有足够空间进行分配时JVM发生一次Minor GC。因为Java对象大多具备朝生夕死的特性,所以Minor GC非常频繁,当然了,其回收速度肯定也是比较快的,与之对应,还有个Full GC或者称为Major GC,是指老年代中的GC,经常会伴随一次Minor GC,Major GC速度一般会比Minor GC速度慢10倍以上!

大对象直接进入老年代

所谓的大对象,是指占用大量连续内存空间的Java对象。最经典的大对象就是那种很长的字符串和数组。大对象对于虚拟机来说是个坏消息,我们写程序时,尽量要避免出现一群朝生夕死的大对象。经常出现大对象容易导致内存还有不少空间时就得提前触发垃圾收集以获取足够的空间来存放大对象。

长期存活的对象将进入老年代

VM采用分代收集思想来管理内存,就要去区分哪些是年轻代的对象,哪些是老年代的对象。我们知道,刚创建的对象肯定是年轻的对象,那么怎么将对象判断为老年代?

在Eden区出生,并经过一次Minor GC后仍然存活,并且能被To Suvivor容纳,移动到To Suvivor区后,年龄设置为1。以后每经历一次Minor GC就将年龄加1,当它的年龄达到一个阀值(默认15,也可以更改-XX:MaxTenurinigThreshold来设置),就会被晋级到老年代中。

动态对象年龄判定

为了更好地适应不同程序内存情况,JVM并不一定是等到对象年龄达到阀值才将对象晋级到老年代。如果在Survivor空间中的相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到指定的阀值。这句话可能有点绕,不太好理解,我来再解释一下,就是说,假设Survivor的空间大小为max,年龄为y的对象总共有n个,如果y*n>max/2,那么所有年龄大于y的对象全部进入到老年代。

空间分配担保

在发生Minor GC之前,虚拟机会先检查老年代可用的连续空间是否大于所有新生代的总空间,如果大于的话,那么这个GC就可以保证安全,如果不成立的,那么可能会造成晋升老年代的时候内存不足。在这样的情况下,虚拟机会先检查HandlePromotionFailure设置值是否允许担保失败,如果是允许的,那么说明虚拟机允许这样的风险存在并坚持运行,然后检查老年代的最大连续可用空间是否大于历次晋升老年代对象的平均大小,如果大于的话,就执行Minor GC,如果小于或者HandlePromotionFailure设置不允许冒险,那么就会先进行一次Full GC将老年代的内存清理出来,然后再判断。

上面提到的风险,是由于新生代因为存活对象采用复制算法,但为了内存利用率,只使用其中的一个Survivor空间,将存活的对象备份到Survivor空间上,一旦出现大量对象在一次Minor GC以后依然存活(最坏的计划就是没有发现有对象死亡需要清理),那么就需要老年代来分担一部分内存,把在Survivor上分配不下的对象直接进入老年代,因为我们不知道实际上具体需要多大内存,我们只能估算一个合理值,这个值采用的方法就是计算出每次晋升老年代的平均内存大小作为参考,如果需要的话,那就提前进行一次Full GC.

取平均值在大多数情况下是可行的,但是因为内存分配的不确定性太多,保不定哪次运行突然出现某些大对象或者Minor GC以后多数对象依然存活,导致内存远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure)。如果出现了HandlePromotionFailure失败,那就只好在失败后重新发起一次Full GC。这样的情况下,担保失败是要付出代价的,大部分情况下都还是会将HandlePromotionFailure开关打开,毕竟失败的几率比较小,这样的担保可以避免Full GC过于频繁,垃圾收集器频繁的启动肯定是不好的。在JDK6之后这是默认打开的。

概述

在之前的博客中,已经对垃圾回收算法的基本知识进行了整理,本篇我们主要介绍在 oracle JVM 中,垃圾回收的具体实现:垃圾回收器Garbage Collector)。在介绍具体的垃圾回收器之前,先介绍一些具体的实现细节知识,方便后面对垃圾回收器的介绍。

阅读全文 »

概述

通过前面介绍Java内存运行时区域,可以学习到程序计数器虚拟机栈本地方法栈 三个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这几个区域的内存分配和回收都具备确定性。在这几个区域内不需要过多考虑回收的问题,因为方法结束或线程结束时,内存自然就跟随着回收了

Java方法区 则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样。我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的垃圾收集器 所关注的是这部分内存。

要对对象进行回收,首先需要解决下面三个事情:

  1. 确定哪些对象可以回收?
  2. 什么时间可以回收?
  3. 如何回收?

本篇文章主要对前面俩个问题进行回答,第三个问题设计具体的回收实现,留到下一篇文章进行讲解。

:后面都是基于java来进行讲解。

阅读全文 »

配置github pages 个人域名

向你的 Github Pages 仓库添加一个CNAME(一定要*大写*)文件

其中只能包含一个顶级域名,像这样:

1
2
example.com

阅读全文 »