为了更好的走读Java concurrent包源码,首先普及下基础知识:volatile、CAS、LockSupport。
concurrent包的实现示意图
volatile
保证可见性,但是不保证原子性(i++并发出问题)
禁止指令重排序
指令重排序推荐阅读
CAS
简介
CAS(Compare And Swap))顾名思义为比较并交换。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了 “ 我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
目的
利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。
问题
ABA问题
如果V的值先由A变成B,再由B变成A,我们最终看到的是A,但是是发生变化了。我们可以通过加时间戳或者版本号来解决。循环时间长开销大
如果JVM能支持处理器提供的pause指令那么效率会有一定的提升。只能保证一个共享变量的原子操作
我们可以通过把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。
温馨提示:感兴趣的同学可以通过AtomicInteger等原子类源码来了解CAS的具体实践
LockSupport
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在进程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。
LockSupport问题
-
LockSupport.park()和unpark(),与object.wait()和notify()的区别?
1.1 面向的主体不一样。LockSuport主要是针对Thread进进行阻塞处理,可以指定阻塞队列的目标对象,每次可以指定具体的线程唤醒。Object.wait()是以对象为纬度,阻塞当前的线程和唤醒单个(随机)或者所有线程。
1.2 实现机制不同。虽然LockSuport可以指定monitor的object对象,但和object.wait(),两者的阻塞队列并不交叉。可以看下测试例子。object.notifyAll()不能唤醒LockSupport的阻塞Thread.
LockSupport能响应Thread.interrupt()事件不?会抛出InterruptedException异常?
能响应interrupt事件,但不会抛出InterruptedException异常。
//问题2
public class LockSupportInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable() {
private int count = 0;
@Override
public void run()
{
long start = System.currentTimeMillis();
long end = 0;
while ((end - start) <= 1000)
{
count++;
end = System.currentTimeMillis();
}
System.out.println("after 1 second.count=" + count);
//等待或许许可
LockSupport.park();
System.out.println("thread over." + Thread.currentThread().isInterrupted());
}
});
t.start();
Thread.sleep(2000);
// 中断线程
t.interrupt();
System.out.println("main over");
}
}