原子类总结and-Git提交出现error: src refspec master does not match any的问题
在本地与远程分别新建了一个仓库并且关联后,想要把本地的文件提交到github上面,
输入下方命令后没有成功,而是出现了报错:error: src refspec master does not match any
git push -u origin master
原因,此时提交所在的分支是一个空目录,没有任何内容所以无法完成提交,所以应该先将待提交的内容添加到仓库并且提交以后再推到远程仓库,即
git add .
git commit -m "XXX"
git push -u origin master
什么是原子类,有什么作用?
不可分割性,一个操作是不可中断的,即便是多线程的情况下也可以保证。Java当中java.util.concurrent.atomic包下的都是具有原子特性的类。
原子类的作用和锁是类似的,都是为了保证并发情况下线程安全。不过原子类相比于锁,有一定的优势:
-
锁粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度。锁的粒度小就不会锁住无用代码,可以提高并发效率。
-
效率更高:通常,使用原子类的效率会比使用锁的效率更高,除非是高度竞争的情况下。所以需要根据场景进行选择。
6类原子类纵览
-
Atomic*基本类型原子类
AtomicInteger、AtomicLong、AtomicBoolean
-
Atomic*Array数组类型原子类
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
-
Atomic*Reference引用类型原子类
AtomicReference、AtomicStampedReference、AtomicMarkableReference
-
Atomic*FieldUpdater升级类型原子类
可以把一个不具备原子性的普通变量升级为原子性的。
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
-
Adder累加器
累加效果很好
LongAdder、DoubleAdder
-
Accumulator累加器
在Adder上又增加了一些额外的功能,只是用到的并不多。
LongAccumulator、DoubleAccumulator
Atomic*基本类型原子类 ,以AtomicInteger为例
其实是对int或者Integer类型的一个封装,封装以后提供了原子的访问和更新操作,原理是基于CAS算法。
常用方法:
public final int get()//获取当前值
public final int getAndSet(int newValue)//获取当前值并设置新的值
public final int getAndIncrement()//获取当前值并自增
public final int getAndDecrement()//获取当前值并自减
public final int getAndAdd(int delta)//获取当前值,并且加上预期的值
public final boolean compareAndSet(int expectedValue, int newValue) //如果当前的数值等于预期值,就以原子方式将这个值设置为新值(newValue),否则就不操作
Atomic*Reference引用类型原子类
AtomicReference:AtomicReference类的作用,和AtomicInteger并没有本质区别,AtomicInteger可以让一个整数保证原子性,而AtomicInteger可以让一个对象保证原子性,当然,AtomicReference的功能明显比AtomicInteger强,因为一个对象里可以包含很多属性。用法和AtomicInteger类似。
AtomicReference在自旋锁的应用:
public class AtomicReference<V> implements java.io.Serializable {
private static final VarHandle VALUE;
//省略
public final boolean compareAndSet(V expectedValue, V newValue) {
return VALUE.compareAndSet(this, expectedValue, newValue);
}
}
public abstract class VarHandle {
public final native
把普通变量升级为原子类:用AtomicIntegerFieldUpdater升级原有变量
辅助自己的一个普通变量,让他也能够具有原子性。
使用场景:当我们大多数情况下需要的是一个普通类型,只有少数情况下,偶尔需要一个原子get-set操作。毕竟原子的消耗还是比普通类型要大的。
关于这个工具的使用:
public class Candidate {
volatile int score;
}
AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");//传入对应的类名和要升级的变量名
scoreUpdater.getAndIncrement(tom);//进行相应的原子操作,例如自增
AtomicIntegerFieldUpdater注意点:
-
可见范围
背后的原理利用了反射,被升级的变量需要具有可见性,而如果是被private修饰,这个操作就不能成功。
-
不支持static
被升级变量不支持被static修饰,否则会报IllegalArgumentException异常。
Adder累加器
Adder累加器是在Java8引入的,相对而言是个较新的类。高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间。竞争激烈的时候,LongAdder把不同线程对应到不同的Cell(一个内部结构)上进行修改,降低冲突的概率,是多段锁的理念,提高了并发性。
-
使用效果:假如说多线程情况下AtomicLong被多个线程累加,每一次加法,都要flush和refresh,很耗费资源。因为在AtomicLong当中为了保持多核之间的数据一致性,每次操作完会把数据刷回主内存,同样每次由从主内存中读取,进行一个间接通信,所以会慢一些。 在内部,LongAdder的实现原理和AtomicLong是不同的,AtomicLong每一次加法都需要做同步,所以在高并发的时候导致冲突比较多,也就降低了效率。而LongAdder,每个线程都会有一个自己的计数器,仅仅用来在自己的线程中计数,这样一来就不会被其他线程的计数器干扰,不同线程的操作之间不存在竞争关系,所以在加和的过程中,根本不需要同步机制,也不需要flush和refresh,没有一个统一的counter来给所有线程统一计数。
-
LongAdder的改进原理:
LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数,竞争不激烈时,直接累加到base变量上;竞争激烈时,各个线程分散累加到自己的槽Cell[i]中,通过计算哈希值给每个线程分配不同的Cell,每个Cell都是一个独立的计数器,这样就不会和其他线程产生干扰了,Cell之间也不存在任何的竞争关系,所以操作中就大大降低了flush和refresh操作,这就是LongAdder吞吐量大的原因,空间换时间。
参考文章:LongAdder解析
-
适用场景:
在低争用情况下,AtomicLong和LongAdder两个类具有类似的特征。但是在竞争激烈的情况下,LongAdder的预期吞吐量要高得多,但是同时也消耗更多空间。LongAdder适合的场景是统计求和和计数的场景,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas方法。
Accumulator累加器
Accumulator和Adder非常相似,Accumulator就是一个更通用版本的Adder。