文章目录
原子类与作用
原子特性:一组操作时不可以被打断的,要不没进行,要不完整的完成。即使在多线程的情况下。
特点:粒度更细,更轻量。但是在高度竞争情况下效率降低。
六种原子类
AtomicInteger
生产实例AtomicInteger atomicInteger = new AtomicInteger();
实例具有的方法:
get()
: 获取当前值getAndSet(int val)
: 获取当前值,并且设置新的值getAndIncrement()
: 获取当前值,并且自增。A++getAndDecrement()
: 获取当前值,并且自减。A–getAndAdd(int delta)
:可以加很多compareAndSet(int expect, int update)
: CAS的操作
AtomicIntegerArray
生产实例AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray (1000);
生成一个数组,这个数组中的数字都是整形,可以被原子操作更新的。
实例具有的方法:
get(index)
: 获取当前值getAndSet(index,int val)
: 获取当前值,并且设置新的值getAndIncrement(index)
: 获取当前值,并且自增。A++getAndDecrement(index)
: 获取当前值,并且自减。A–getAndAdd(index, int delta)
:可以加很多compareAndSet(index, int expect, int update)
: CAS的操作
AtomicReference
让一个对象保持原子性。主要方法就是,compareAndSet(expect, current)
。
生成实例AtomicReference<?> name = new AtomicReference<>();
AtomicIntegerFiledUpdate
把普通变量升级为原子类变量。
- 构造方法:
public class test {
static Candidate tom;
public static AtomicIntegerFieldUpdater<Candidate> socreUpdate = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
public static class Candidate{
volatile int score;
}
// 之后再使用的时候,使用socreUpdate.getAndIncrement(tom);
}
- 方法原理:
实际上它是反射实现的,因此必须是public的变量才可以被修饰升级。并且不支持static方法,同样因为是反射。
LongAdder
是Java 8引用的,再高并发的环境下,LongAdd比AtomicLong的效率高,本质是空间换时间。
构建LongAdder counter = new LongAdd();
,实例方法:.increment()
。这个操作的自加比AtomicLong快。
-
原理:AtomicLong每一次都要进行flush和refresh。 LongAdder不需要每每次都flush,而是在每个线程的内部都有一个自己的计数器,不会和其他线程的计数器冲突。内部实际上有一个
base
变量当不激烈时候,直接累加,否则使用Cell[]
数组,各个线程分散累加到自己的槽里,最后在累加。 -
使用场合:LongAdder使用高并发下的统计求和,方法单一;AtomicLong还有其他的方法。
CAS原理
实现不能被打断的并发操作,Compare and Swap。CAS有三个操作数:内存值V,预期值A,要修改的值B。当且仅当预期值与内存值一样,才将内存值进行修改。每次有一个
本质实现:使用CPU保证原子性。
应用场景:
- 乐观锁,采用cas的方法实现并发
- 并发容器:concurrentHashMap
- 原子类:AtomicInteger,这个类加载
Unsafe
工具,可以直接操作内存数据。并且volatile保证可见性。Unsafe是native方法。
缺点
- ABA问题:5改成7,7又改成5。会错误的认为没有发生修改。解决方法:在使用的时候加上时间标签,
AtomicStampedReference
,除了对比数值还需要对比时间戳。 - 自旋时间比较长:高并发的场景下会消耗资源。
并发与不变性
对一个对象而言,被创建以后,状态不再修改,就是不可变的。这种对象是线程安全的,因为最多只能并发读取,但是不能修改。
final关键字
- 作用:类防止被继承;方法防止被重写,变量防止被修改。
当final
修饰一个对象时候,只是保证了对象的引用地址不会发生改变,但是对象引用的内容还是可能被修改的。
修饰变量
因为final的赋值只能进行一次,因为需要准寻赋值时机。
- final 对于类变量
final必须被初始化赋值。1.直接定义时候,2.构造函数中this.a = a
, 3.初始化代码块中。 - final静态变量
1、直接在等号右边赋值,2.static初始代码块赋值。 - 方法中的final
在使用前进行赋值即可。
修饰方法
- 不能修饰构造方法
- 修饰其他方法,防止被override。静态方法也是不能被重写,但是可以写一个一模一样的静态方法(JVM的静态绑定)
修饰类
典型的就是String
不可以被继承。
不变性与final的关系
- 对于基本数据类,被final修改具有不变性
- 对于对象类型,需要该对象保证自身被创建以后,状态才会永久不可变。
注意:并不是把所有的属性都声明为final就是不可变的,可能存在对象的类型。但是代码中可以保证这个对象不会被操作(对象方法封闭发布没有发生溢出),从而实现不可变。
栈空间
把变量写在线程的内部,线程调用方法,方法内部定义的int
之类的是不线程共享,因此是可以保证线程安全的。