锁
据jvm内存模型,线程共享主存,各变量同步不及时,造成线程不安全,为此java提供了锁来解决线程不安全。
乐观锁
从理论上讲,乐观锁假设各线程不同时修改变量,仅仅通过版本号,时间戳去保证线程安全。java提供的CAS(aompareAndSwap)也是乐观锁的一一种实现
CAS:比较与交换,有3个核心变量,v-内存值,A-期望值,B-修改值,只有当A与v的值相同时才去更新v的值为B,否则不断循环;
ABA问题,当一个线程把数据修改成了B,然后又修改回了A,此时其他线程读取时,认为变量并没有问题,忽略了修改的过程
for (;;) { //不断自旋 int wc = workerCountOf(c);//统计工作线程 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c))//工作线程通过CAS+1 break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop }
悲观锁
从理论上讲,悲观锁假设各线程同时修改变量,通过加锁,线程独占共享变量去保证线程安全。java提供的synchronized,lock的就是悲观锁的一种实现。
synchronized
来自:https://blog.csdn.net/zjy15203167987/article/details/82531772
jvm底层实现,锁对象,保证只有一个线程进去临界区,同时保证共享变量的可见性;但是锁的粒度太大,太占资源,线程等待时间过长。
同步实例方法,锁实例对象,只有获取实例对象才能进去,其他线程只能访问非synchronized的方法
锁静态对象,锁类对象,只有获取类对象才能进去
同步代码块,锁指定对象,只有获取指定对象的锁才能进去
Lock
jdk提供的一种实现,锁的粒度小,可控,但需要加锁与释放锁;
ReentrantLock-可重入锁、独占锁,底层是AQS(AbstractQueuedSynchronizer)同步队列,里面包含的公平与非公平锁的实现
详情可见:https://blog.csdn.net/qq_29373285/article/details/85964460
public ReentrantLock(boolean fair) {//true实现公平锁,false非公平锁 sync = fair ? new FairSync() : new NonfairSync(); }
static final class FairSync extends Sync {//公平锁实现 final void lock() {//获取锁的状态,锁未被占用即可进入 acquire(1); } public final void acquire(int arg) {//cas获取锁,按顺序加入队列 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//cas修改锁状态,设置独占锁 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires;//可重入,当前线程占用锁重入 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }