搭建一个网站的服务器,北京 工业网站建设公司,企业所得税是5%还是25%,广州新际网站建设Java 多线程 --- 锁的概念和类型划分锁的概念乐观锁与悲观锁公平锁与非公平锁什么是可重入锁独占锁与共享锁轻量级锁和重量级锁自旋锁 (Spinlock)锁的概念 锁可以将多个线程对共享数据的并发访问转换为串行访问, 这样一个共享数据一次只能被一个线程访问, 该线程访问结束后其他…
Java 多线程 --- 锁的概念和类型划分锁的概念乐观锁与悲观锁公平锁与非公平锁什么是可重入锁独占锁与共享锁轻量级锁和重量级锁自旋锁 (Spinlock)锁的概念 锁可以将多个线程对共享数据的并发访问转换为串行访问, 这样一个共享数据一次只能被一个线程访问, 该线程访问结束后其他线程才能对其进行访问.锁具有排他性 (Exclusive), 即一个锁一次只能被一个线程持有. 所以这种锁被称为排他锁或者互斥锁 (Mutex). 乐观锁与悲观锁 乐观锁和悲观锁严格的说不是一种锁而是一种策略 悲观锁 加锁是一种悲观的策略它总是认为每次访问共享资源的时候总会发生冲突所以宁愿牺牲性能时间来保证数据安全悲观锁的使用场景并不少见数据库很多地方就用到了这种锁机制比如行锁表锁读锁写锁等都是在做操作之前先上锁悲观锁的实现往往依靠数据库本身的锁功能实现。Java程序中的Synchronized和ReentrantLock等实现的锁也均为悲观锁。 乐观锁 乐观锁就是先不加锁. 无锁是一种乐观的策略它假设线程访问共享资源不会发生冲突所以不需要加锁因此线程将不断执行不需要停止。一旦碰到冲突就重试当前操作直到没有冲突为止无锁的策略之一就是使用CAS机制 CAS机制 CAS的全称是Compare-and-Swap也就是比较并交换它包含了三个参数VABV表示要读写的内存位置A表示旧的预期值B表示新值具体的机制是当执行CAS指令的时候只有当V的值等于预期值A时才会把V的值改为B如果V和A不同有可能是其他的线程修改了这个时候执行CAS的线程就会不断的循环重试直到能成功更新为止 CAS算是比较高效的并发控制手段不会阻塞其他线程。但是这样的更新方式是存在问题的看流程就知道了如果C的结果一直跟预期的结果不一样的话线程A就会一直不断的循环重试重试次数太多的话对CPU也是一笔不小的开销。CAS的ABA问题 CAS还有个问题就是ABA问题比如第一次拿到内存里的值时是然后被其他线程修改为B, 然后又修改为A, 而此时去比较内存里的值会发现没有变但是实际上还是有改动举个通俗点的例子你倒了一杯水放桌子上干了点别的事然后同事把你水喝了又给你重新倒了一杯水你回来看水还在拿起来就喝如果你不管水中间被人喝过只关心水还在还好 但是假若你是一个比较讲卫生的人那你肯定就不高兴了ABA问题的解决思路: 使用版本号。在变量前面追加上版本号每次变量更新的时候把版本号加1那么A→B→A就会变成1A→2B→3A了 公平锁与非公平锁
公平锁 公平锁的概念是如果当前一个线程已经获取到锁了其他线程如果再想获取到锁的话需要排队. 非公平锁 非公平锁的概念是如果当前一个线程已经获取到锁了那么新来的线程如果再想获取到锁先CAS抢一下如果抢到了就执行代码抢不到在去排队 Java中的公平锁和非公平锁 JDK中的ReentrantLock既支持非公平锁又支持公平锁默认非公平锁Synchronized则是非公平锁 优缺点 公平锁的优点是等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低等待队列中除第一个线程以外的所有线程都会阻塞CPU 唤醒阻塞线程的开销比非公平锁大 非公平锁的优点是可以减少唤起线程的开销整体的吞吐效率高因为线程有几率不阻塞直接获得锁CPU 不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死或者等很久才会获得锁 什么是可重入锁 所谓重入锁即一个线程如果获取到了锁那么这个线程再下一次进入同步代码中的时候可以直接进入不用重新获取锁我们最熟悉的sychronized和ReentrantLock都是可重入锁。其实从ReentrantLock的名称上就可以看出来Reentrant这个单词翻译成中文就是可重入的意思. ReentrantLock可重入锁的实现记录一下当前获取锁的线程记录为ownerThread如果当前线程在获取锁的时候发现自己就ownerThread那么当前线程可以不用去抢锁直接执行 独占锁与共享锁 独占锁的概念是如果有一个线程已经获取到了锁其他线程不可以继续获取锁锁只能有此线程独占。 共享锁的概念是一个锁可以有多个线程共享即一个线程获取到了锁其他线程还可以继续获取锁 基于AQS实现的ReentrantLock就是独占锁而AQS也提供了实现共享锁的模版方法tryAcquireShared. 轻量级锁和重量级锁 重量级锁的概念是如果锁已经被持有了当前线程获取不到锁当前线程挂起等待锁的释放以及被唤醒。 轻量级锁的概念是如果锁已经被持有了当前线程获取不到锁那么将使用CAS机制或者自旋的方式获取锁 (在Java中Synchronized的轻量级锁是用自旋锁实现的)这样设计的原因是大部分情况下我们占用锁的线程很快就执行完了在很短的时间内就释放了锁 如果使用重量级锁那么下一个线程想获取锁继续执行的话需要经历挂起以及唤醒这个过程需要CPU上下文切换这个时间开销甚至大于用户代码执行的时间所以轻量级锁让线程等一会锁一旦释放当前线程可以立马获取到省去了不必要的上下文切换的开销 JVM对Synchronized锁的优化就是从无锁到重量级锁的升级过程无锁-偏向锁-轻量级锁-重量级锁 自旋锁 (Spinlock) 自旋锁spinlock是指当一个线程在获取锁的时候如果锁已经被其它线程获取那么该线程将循环等待然后不断的判断锁是否能够被成功获取直到获取到锁才会退出循环。 获取锁的线程一直处于活跃状态但是并没有执行任何有效的任务使用这种锁会造成busy-waiting。