线程的同步是为了防止对个线程访问同一个对象对数据造成损坏。
线程安全问题的前提是多个线程并发访问同一个共享变量和资源,那么就能得到让一个共享变量在一个时间只能被一个线程访问,结束后才能被其他的线程访问。
Lock锁就是利用这种机制来实现的保护线程安全的线同步机制。
锁分为两种
保护共享变量资源的数据安全实现线程安全,作用是保障数据的原子性,可见性,有序性。
b = a + 1;
c = 2;
flag = true;
由于锁能够保障有序性,因此对于上面例子,可几种情况(读线程无法区分更新顺序):
如果一个读线程在临界区中读取到c =2,flag =ture, b = a+1.
如果一个读线程在临界区中读取到flag = true,c=2, b = a+1
因此:尽管锁能够保障有序性,但是这并不意味着临界区内的内存操作不能够被重排序。
临界区内的任意两个操作依然可以在临界区之内被重排序(即不会重排序到临界区之外)。
由于临界区内的操作具有原子性,写线程在临界区内对各个共享数据的更新同时对读线程可见,因此这种重排序并不会对其他线程产生影响。
1. 锁的可重入性:一个线程在持有一个锁的时候,能否在次或者多吃申请该锁,如果一个线程在持有一个锁的时候,还能继续申请该锁,那么我们就称呼为这种锁为可重入的锁。否则锁就是非可重入的锁
//伪代码
void A(){
cquireLock(Lock); // 申请锁lock
//省略....
methodB(); // 再次调用methodB ,申请锁lock
releaseLock(lock); // 释放锁
}
void B(){
acquireLock(lock); // 申请锁Lock
//...
releaseLock(lock); // 释放锁Lock
}
方法A使用了锁lock, 该锁引导的临界区代码又调用了另外 一个方法B, 而方法B也使用lock。
那么这就产生了一个问题:方法A 的执行线程持有锁lock 的时候调用了 方法B, 而方法B执行的时候又去申请锁lock , 而lock 此时正被当前线程持有(未被释放)。那么,此时方法B究竟能否获得(申请成功)lock 呢?可重入性就描述了这样一个问题。
可重入实现锁原理
可重入锁可以理解为一个对象,它拥有一个计数器,初始值为0,表示没有被任何线程持有;每一个线程持有锁的时候该计数器就会加1,释放锁就会减一。一个可重入锁初次获得该锁的开销相对大,这是因为该锁的线程必须和其他线程竞争来获取锁,可重入锁的持有线程后续获得锁的开销就会小,因为java虚拟机只需要咋响应的计数器上加1就行,即可获得锁。
3、锁的粒度
一个锁可以保护一个或者多个共享数据,一个锁实例保护的的共享数据的数量大小就是锁粒度
4.锁的开销和及其可能得问题
4.锁的适用场景
如果其中有线程操作可以使用锁
参考链接锁相关介绍