国家建设网站,如何做企业网站加v,北京手机网站建设费用,门户网站建站目标思维导图文章已收录Github精选#xff0c;欢迎Star#xff1a;https://github.com/yehongzhi/learningSummary悲观锁悲观锁是平时开发中经常用到的一种锁#xff0c;比如ReentrantLock和synchronized等就是这种思想的体现#xff0c;它总是假设别的线程在拿线程的时候都会修…思维导图文章已收录Github精选欢迎Starhttps://github.com/yehongzhi/learningSummary悲观锁悲观锁是平时开发中经常用到的一种锁比如ReentrantLock和synchronized等就是这种思想的体现它总是假设别的线程在拿线程的时候都会修改数据所以每次拿到数据的时候都会上锁这样别的线程想拿这个数据就会被阻塞。如图所示synchronized是悲观锁的一种实现一般我们都会有这样使用private static Object monitor new Object();public static void main(String[] args) throws Exception {//锁一段代码块synchronized (monitor){}
}
//锁实例方法锁对象是this即该类实例本身
public synchronized void doSome(){}
//锁静态方法锁对象是该类即XXX.class
public synchronized static void add(){}
我们以最简单的同步代码块来分析其实就是将synchronized作用于一个给定的实例对象monitor即当前实例对象就是锁对象每次当线程进入synchronized包裹的代码块时就会要求当前线程持有monitor实例对象锁如果当前有其他线程正持有该对象锁那么新到的线程就必须等待这样也就保证了每次只有一个线程执行synchronized内包裹的代码块。从上面的分析中可以看出悲观锁是独占和排他的只要操作资源都会对资源进行加锁。假设读多写少的情况下使用悲观锁的效果就不是很好。这时就引出了接下来要讲的乐观锁。乐观锁乐观锁顾名思义它总是假设最好的情况线程每次去拿数据时都认为别人不会修改所以不会上锁但是在更新的时候会判断一下在此期间别人有没有去更新这个数据如果这个数据没有被更新当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新则根据不同的实现方式执行不同的操作例如报错或者自动重试。如图所示一般乐观锁在java中是通过无锁编程实现的最常见的就是CAS算法比如Java并发包中的原子类的递增操作就是通过CAS算法实现的。CAS算法其实就是Compare And Swap(比较与交换)的意思。目的就是将内存的值更新为需要的值但是有个条件内存值必须与期待的原内存值相同。展开来说我们有三个变量内存值M期望的内存值E更新值U只有当ME时才会将M更新为U。CAS算法实现的乐观锁在很多地方有应用比如并发包的原子类AtomicInteger类。在自增的时候就使用到CAS算法。public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);
}//var1 是this指针
//var2 是偏移量
//var4 是自增量
public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {//获取内存称之为期待的内存值Evar5 this.getIntVolatile(var1, var2);//var5 var4的结果是更新值U//这里使用JNI方法每个线程将自己内存中的内存值M与var5期望值比较//如果相同则更新为var5 var4返回true跳出循环。//如果不相同则把内存值M更新为最新的内存值然后自旋直到更新成功为止} while(!this.compareAndSwapInt(var1, var2, var5, var5 var4));//返回更新后的值return var5;
}public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
所以可以看出CAS算法其实是无锁的。好处是在读多写少的情况下性能是比较好的。那么CAS算法的缺点其实也是很明显的。ABA问题。线程C将内存值A改成了B后又改成了A而线程D会认为内存值A没有改变过这个问题就称为ABA问题。解决办法很简单在变量前面加上版本号每次变量更新的时候变量的版本号都1即A-B-A就变成了1A-2B-3A。在写多读少的情况下也就是频繁更新数据那么会导致其他线程经常更新失败那么就会进入自旋自旋时会占用CPU资源。如果资源竞争激烈多线程自旋的时间长导致消耗资源。使用场景在读多写少的场景下更新时很少发生冲突使用乐观锁减少了上锁和释放锁的开销可以有效地提升系统的性能。相反在写多读少的场景下如果使用乐观锁会导致更新时经常产生冲突然后线程会循环重试这样会增大CPU的消耗。在这种情况下建议可以使用悲观锁。总结在日常的开发中悲观锁和乐观锁应该是见得最多用得最多的锁比如最常见的synchronized和ReentrantLock是悲观锁并发包中的原子类和ConcurrentHashMap则用了乐观锁。锁的实现并不复杂关键是搞懂这两种锁的思想这样才能在合适的地方使用合适的锁。这篇文章就讲到这里了希望看完后能有所收获感谢你的阅读。觉得有用就点个赞吧你的点赞是我创作的最大动力~我是一个努力让大家记住的程序员。我们下期再见能力有限如果有什么错误或者不当之处请大家批评指正一起学习交流