Volatile不能保证原子性
public class VolatileNotSync {
volatile int count = 0;
void m() {
for (int i = 0; i < 10000; i++) count++;
}
public static void main(String[] args) {
T04_VolatileNotSync t = new T04_VolatileNotSync();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t::m, "thread-" + i));
}
threads.forEach((o) -> o.start());
threads.forEach((o) -> {
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
输出结果如下(如果能保证原子性的话则应输出100000):
锁的细化DEMO
public class FineCoarseLock {
int count = 0;
synchronized void m1() {
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁
count++;
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void m2() {
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁
//采用细粒度的锁,可以使线程争用时间变短,从而提高效率
synchronized (this) {
count++;
}
//do sth need not sync
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
应该避免锁定对象的引用变成另外的对象
锁定某个对象o,如果o的属性发生改变,不影响锁的使用,但是如果o变成另外一个对象,则锁定的对象发生改变,因为对象做锁的时候,它的锁的标记位在markword上面,如果锁的对象发生改变,则它的markword标记也发生改变了,就会导致程序出现问题
代码如下:
public class SyncSameObject {
/*final*/ Object o = new Object();
void m() {
synchronized (o) {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
SyncSameObject t = new SyncSameObject();
new Thread(t::m, "t1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(t::m, "t2");
t.o = new Object(); //锁对象发生改变,所以t2线程得以执行,如果注释掉这句话,线程2将永远得不到执行机会
t2.start();
}
}
CAS的ABA问题理解
如果是基础类型,ABA问题无所谓,如果是引用类型就需要去解决了
ABA问题解决方式:加Version版本号控制,采用AtomicStampedReference。
ABA问题造成的后果: 假设有 A、B、C这三个对象,A这个对象指向B,B又指向C调用C的逻辑,如果采用CAS的方式,先让A指向C,然后对C对象里面的业务逻辑做一些更改,再让A重新指向B,然后另一个线程再直接通过A调用B然后再调用C,可能里面的执行结果,就受上一个线程的执行结果所干扰了。
其中AutomicInteger的incrementAndGet() 方法最终调用了Unsafe类的compareAndSwapInt方法
Unsafe类的JDK8版本实现:
直接操作内存
- allocateMemory putXX freeMemory pageSize
直接生成类实例 - allocateInstance
直接操作类或实例变量
-objectFieldOffset
-getInt
-getObject
CAS相关操作
-compareAndSwapObject Int Long