目录
一、AtomicInteger
public class T01_AtomicInteger {
//不用加volatile
public AtomicInteger count = new AtomicInteger(0);
//不用加synchronized
public void m() {
for (int i = 0; i < 1000; i++) {
//count++
count.incrementAndGet();
}
}
public static void main(String[] args) {
T01_AtomicInteger t = new T01_AtomicInteger();
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(t::m, "t" + i));
}
threadList.forEach((o)->{
o.start();
});
threadList.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
1、底层实现
CAS(Compare And Swap/Set)无锁优化、乐观锁
cas(V, Expected, NewValue)
if V == E
V = New
otherwise try again or fail
V:要改的值
Expected:期望当前这个值是多少
NewValue:要设置的新值
比如要改的值是3(即最开始拿到的这个值是3),当执行CAS操作时,我期望(Expected)这个值是3,是3我才修改这个值;如果当执行CAS时,不等于我期望的值3,说明这个值被其他线程改了(假如改为了4),那我就再试一次(try again)。此时我期望这个值是4,如果执行CAS操作时,没有其他的线程再次修改这个值,即和我期望的值4相等,那我再执行CAS操作,把值修改为新值。
(1) 如果在执行CAS操作的时候,在判断值是否为我期望的值后,马上有其他线程把这个值改为了其他的值,那这种情况不是依然有问题吗?
CAS操作是CPU原语支持的,也就是说CAS的操作是CPU指令级别的操作,不允许被改变。
(2)ABA问题
就是在我执行CAS操作的时候,这个值被其他的线程修改为了2,然后又改为了3,也就是中间经过了更改。如果是int这种无关系的类型,就可以不用管;如果要处理,需要加版本号,也就是这个值作任何一次的修改,版本号都加1,后面检查的时候和版本号一起检查。
(就好比 你的女朋友跟你复合时,中间又交了别的男朋友,她可能就已经变了,不再是你原来的女朋友)
(3)CAS底层是怎么做到的?
里面是通过sun.misc.Unsafe类来做的(大多数方法都是native的)。该类主要作用:直接操作JVM内存(native allocateMemory)、直接生成类实例(native allocateInstance)、直接操作变量(native getInt、native getObject)、以及CAS相关操作(native compareAndSwapObject)。该类只能通过反射或者getUnsafe得到该类的对象来使用(单例),不能直接使用。
2、SyncLong VS AtomicLong VS LongAdder
public class T02_AtomicSynclongLongAdder {
private static AtomicLong count1 = new AtomicLong(0L);
private static long count2 = 0L;
private static LongAdder count3 = new LongAdder();
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[1000];
//AtomicLong
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100000; j++) {
count1.incrementAndGet();
}
});
}
long start = System.currentTimeMillis();
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
long end = System.currentTimeMillis();
System.out.println("AtomicLong: " + count1.get() + " time: " + (end - start));
//long
Object o = new Object();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 100000; j++)