1、voliate
voliate赋予变量在多线程中的可见性,只能作用于变量,非堵塞。java内存模型(如下图)描述了多线程之间信息交换和同步的方式:每个线程都从主内存load一份数据到自己的工作内存,线程对变量的读写操作都是在工作内存中进行的,然后在save到主内存。
如果多线程同时操作主内存同意拷贝变量a,那么就可能导致变量的值乱掉,voliate保证了voliate变量值修改后的新值立即同步到主内存,每次使用变量也都从主内存刷新数据,即保证了数据在线程间的可见性以及禁止指令重排序。
2、synchronized
synchronize通过加锁堵塞的方式来实现同步,可以修饰变量,方法以及代码块.
Synchronized 的语义底层是通过一个 Monitor 的对象来完成,
每个对象有一个监视器锁(Monitor),当 Monitor 被占用时就会处于锁定状态。 线程执行 Monitorenter 指令时尝试获取 Monitor 的所有权,过程如下: 如果 Monitor 的进入数为 0,则该线程进入 Monitor,然后将进入数设置为 1,该线程即为 Monitor 的所有者。
如果线程已经占有该 Monitor,只是重新进入,则进入 Monitor 的进入数加 1。
如果其他线程已经占用了 Monitor,则该线程进入阻塞状态,直到 Monitor 的进入数为 0,再重新尝试获取 Monitor 的所有权。
执行 Monitorexit 的线程必须是 Objectref 所对应的 Monitor 的所有者。 指令执行时,Monitor 的进入数减 1,如果减 1 后进入数为 0,那线程退出 Monitor,不再是这个 Monitor 的所有者
3、AtomicInteger
AtomicInteger的本质:自旋锁+Unsafe的CAS原子操作,非堵塞同步方式(同步指的是一直等待结果,非堵塞是指可以做其他的事情,并不释放cpu资源。)。
网上的一则故事比较生动地讲堵塞与同步
故事:老王烧开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
老王想了想,有好几种等待方式
1.老王用水壶煮水,并且站在那里,不管水开没开,每隔一定时间看看水开了没。-同步阻塞
老王想了想,这种方法不够聪明。
2.老王还是用水壶煮水,不再傻傻的站在那里看水开,跑去寝室上网,但是还是会每隔一段时间过来看看水开了没有,水没有开就走人。-同步非阻塞
老王想了想,现在的方法聪明了些,但是还是不够好。
3.老王这次使用高大上的响水壶来煮水,站在那里,但是不会再每隔一段时间去看水开,而是等水开了,水壶会自动的通知他。-异步阻塞
老王想了想,不会呀,既然水壶可以通知我,那我为什么还要傻傻的站在那里等呢,嗯,得换个方法。
4.老王还是使用响水壶煮水,跑到客厅上网去,等着响水壶自己把水煮熟了以后通知他。-异步非阻塞
老王豁然,这下感觉轻松了很多。
对于互斥锁这样的悲观锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁是乐观锁,它不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
复制代码
优点:通过CAS保证了原子性
缺点:消耗CPU性能
4、ReentrantLock
重入锁底层实现是AbstractQueuedSynchronizer,简称AQS。synchronized是基于JVM层面实现的,而Lock是基于JDK层面实现的。相比synchronized,ReentrantLock可以进行锁的超时和中断设置。重入性是指如果以获取锁的线程再次去获取锁,那么就会获取锁成功,获取锁成功次数加1,后面释放锁锁的次数必须等于之前成所获取锁的的次数,那么该锁才算完全释放。
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) { //获取锁
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { //锁是否被占用
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //占用锁的线程是否时当前线程
int nextc = c + acquires; //如果是,则获取锁次数+1
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) { //释放锁
int c = getState() - releases; //上辈子造的孽,一个个减吧
if (Thread.currentThread() != getExclusiveOwnerThread()) //占用锁的线程是否时当前线程
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true; //真正释放了
setExclusiveOwnerThread(null); //真正释放了
}
setState(c);
return free;
}
复制代码