* ## **重入锁**
重入锁也称递归锁,指的是同一线程外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但却不会受到影响,ReentrantLock 和synchronized都是可重入锁。
```
class TaskThread implements Runnable {
public synchronized void set() {
System.out.println(Thread.currentThread().getName() + ":set");
}
public synchronized void get() {
System.out.println(Thread.currentThread().getName() + ":get");
set();
}
@Override
public void run() {
get();
}
}
class test {
public static void main(String[] args) {
TaskThread taskThread = new TaskThread();
Thread t1 = new Thread(taskThread);
Thread t2 = new Thread(taskThread);
t1.start();
t2.start();
}
}
```
执行结果
```
Thread-0:get
Thread-0:set
Thread-1:get
Thread-1:set
```
get方法中调用set方法,2个方法都加入了synchronized锁,但是并没有产生死锁的现象。说明set方法能获取到上层的get的锁。
* ## **读写锁**
ReentrantLock和synchronized都为排它锁,在同一时刻只允许一个线程访问锁资源,而读写锁在同一时刻可以允许多个读线程访问,在写线程访问的时候其他的读线程和写线程都会被阻塞,读写锁维护一对锁(读锁和写锁),通过锁的分离,使得并发性提高,适用于读多写少的应用场景。
```
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
// 读锁
Lock r = rwl.readLock();
// 写锁
Lock w = rwl.writeLock();
```
* ## **悲观锁与乐观锁**
### 乐观锁
不会上锁的一种锁,通常采用添加version版本字段的方式,每次修改前都会对比版本号,修改后version+1
### 悲观锁
每次读取数据都认为其他线程会修改数据,所以都会加锁,synchronized也算是悲观锁,还有数据库的行锁,读锁,写锁也都是悲观锁。
* ## **原子类**
线程安全的3大特性之一就是原子性,要想保证线程原子性,我们通常会采用加入synchronized的方式。但是synchronized锁会产生阻塞,必定会影响程序性能。对于一些相对标准化的共享变量的多线程操作,比如i++等,java并发包提供了一种更加有效率的方式.。
```
private AtomicInteger count = new AtomicInteger();
...
count.incrementAndGet()
...
```
通过创建AtomicInteger对象,调用incrementAndGet方法就能实现线程安全的非阻塞式的原子操作,java.util.concurrent.atomic原子操作工具包中还提供了很多其他类型的操作做。
```
AtomicInteger
AtomicBoolean
AtomicLong
AtomicReference
```
这些原子类中都是用了无锁的概念,大多使用CAS无锁机制来实现底层原理。
* ## **CAS无锁机制**
CAS是英文单词**Compare And Swap**的缩写,翻译过来就是比较并替换。
CAS的三个核心参数CAS(V,E,N):
```
1. 内存地址V:共享变量的内存地址,jmm模型中可以理解为共享变量存放在主内存中值。
2. 预期值E:jmm模型中可以理解为保存在线程本地缓存中的值。
3. 新值N:线程经过运算后,即将要写入到主内存中的值。
```
CAS机制中,更新一个变量的时候,只有当变量的预期值E和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为N。举个例子:
```
1.在内存地址V当中,存储着值为5的变量。
2.此时线程1想要把变量的值增加1。对线程1来说,预期值E=5,要修改的新值N=6。
3.但是,在线程1要提交更新之前,另一个线程2把内存地址V中的变量值率先更新成了6。
4.此时,线程1开始提交更新,首先进行E和地址V的实际值比较(Compare),发现E不等于V的实际值,提交失败。
5.线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,E=6,N=7。
6.这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现E和地址V的实际值是相等的。
7.线程1进行替换,把地址V的值替换为N,也就是7。
```
知道了CAS的执行机制,再来分析`AtomicInteger
`的源码
```
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// 用来实现CAS机制的实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 共享变量在主内存中偏移量,可以理解是V值
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
...
// 自增的方法
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
...
}
```
再来看`getAndAddInt`方法的内部实现
```
// 原生方法实现CAS算法
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
// 原生方法获取期望值E
public native int getIntVolatile(Object var1, long var2);
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
// 调用原生方法获取期望值E
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
```
代码可以理解为
```
do {
获取期望值E
} while(!CAS(内存地址V, 期望值E, 新值N));
return 期望值E;
```
CAS机制的缺点
问题:如果变量V初次读取的时候是5,并且在准备赋值的时候检查到它仍然是5,那能说明它的值没有被其他线程修改过了吗?
如果在这段期间曾经被改成6,然后又改回5,那CAS操作就会误认为它从来没有被修改过。但是这对当前线程的执行会有影响吗????
* ## **Synchronized实现原理**
未完待续,貌似挺复杂
* ## **Lock实现原理与AQS**
未完待续,貌似挺复杂
* ## **自旋锁**
自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里,会持续占用CPU资源,自旋锁适用于锁使用者保持锁时间比较短的情况。CAS属于自旋锁模式
* ## **Disruptor并发框架**
能够在一个线程里每秒处理6百万订单的开源并发框架。附上链接
[http://ifeve.com/disruptor/](http://ifeve.com/disruptor/)