做图片网站赚不赚钱,wordpress关于我们,wordpress切换回老的编辑器,有哪些网站可以做淘宝客目录 引出Java中锁升级synchronized与lock锁区别 缓存三兄弟#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出
Java进阶#xff08;锁#xff09;——锁的升级#xff0c;synchronized与lock锁区别 Java中锁升级 看一段代码#xff1a;
public class… 目录 引出Java中锁升级synchronized与lock锁区别 缓存三兄弟缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出
Java进阶锁——锁的升级synchronized与lock锁区别 Java中锁升级 看一段代码
public class App {public static void main(String[] args) throws InterruptedException {Calculate cal new Calculate();long start System.currentTimeMillis();Thread t1 new Thread(()-{for (int i 0; i 1000_0000; i) {cal.increase();}});Thread t2 new Thread(()-{for (int i 0; i 1000_0000; i) {cal.increase();}});t1.start();t2.start();t1.join();t2.join();System.out.println(time (System.currentTimeMillis()-start));System.out.println(cal.getNum() cal.getNum());}
}
public class Calculate {private int num;public int getNum() {return num;}// 多线程执行public void increase() 会有线程安全问题// synchronized 锁解决锁的是什么public void increase(){synchronized (this) {num;}}
}分析 对象组成 Mark Word用于存储对象自身的运行时数据如哈希码HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等占用内存大小与虚拟机位长一致。Mark Word对应的类型是markOop。源码位于markOop.hpp中。在64位虚拟机下Mark Word是64bit大小的其存储结构如下
对象头在64位虚拟机 8个字节 package cn.wn.juc;
import org.openjdk.jol.info.ClassLayout;
import java.util.concurrent.TimeUnit;
public class App {// 锁升级演示public static void main(String[] args) throws InterruptedException {User user01 new User();System.out.println(无状态001 ClassLayout.parseInstance(user01).toPrintable());// 从jdk6开始jvm默认延迟4s自动开启开启偏向锁。通过-XX:BiasedLockingStartupDelay0设置取消延迟// 如果不要偏向锁-XX:-UseBiasedLockingfalseTimeUnit.SECONDS.sleep(5);User user02 new User();System.out.println(启用偏向锁101 ClassLayout.parseInstance(user02).toPrintable());for (int i 0; i 2; i) {synchronized (user02) {System.out.println(偏向锁101带线程ID ClassLayout.parseInstance(user02).toPrintable());}// 偏向锁释放对象头不会变化一直存在 偏向线程id 下次执行判断是否同一个线程如果是直接执行System.out.println(偏向锁101释放线程ID ClassLayout.parseInstance(user02).toPrintable());}// 多个线程加锁升级为轻量级锁new Thread(() - {synchronized (user02) {System.out.println(轻量级锁00 ClassLayout.parseInstance(user02).toPrintable());try {System.out.println(休眠3秒);TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(轻量--重量10 ClassLayout.parseInstance(user02).toPrintable());}}).start();TimeUnit.SECONDS.sleep(1);new Thread(() - {synchronized (user02) {System.out.println(重量级锁10 ClassLayout.parseInstance(user02).toPrintable());}}).start();}
}dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.10/version
/dependency锁的状态总共有四种级别由低到高依次为无锁、偏向锁、轻量级锁、重量级锁这四种锁状态分别代表什么为什么会有锁升级其实在 JDK 1.6之前synchronized 还是一个重量级锁是一个效率比较低下的锁但是在JDK 1.6后Jvm为了提高锁的获取与释放效率对synchronized 进行了优化引入了 偏向锁 和 轻量级锁 从此以后锁的状态就有了四种无锁、偏向锁、轻量级锁、重量级锁并且四种状态会随着竞争的情况逐渐升级而且是不可逆的过程即不可降级也就是说只能进行锁升级从低级别到高级别不能锁降级高级别到低级别意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略目的是为了提高获得锁和释放锁的效率。
无锁
无锁是指没有对资源进行锁定所有的线程都能访问并修改同一个资源但同时只有一个线程能修改成功。
无锁的特点是修改操作会在循环内进行线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出否则就会继续循环尝试。如果有多个线程修改同一个值必定会有一个线程能修改成功而其他修改失败的线程会不断重试直到修改成功。
偏向锁
初次执行到synchronized代码块的时候锁对象变成偏向锁通过CAS修改对象头里的锁标志位字面意思是“偏向于第一个获得它的线程”的锁。执行完同步代码块后线程并不会主动释放偏向锁。当第二次到达同步代码块时线程会判断此时持有锁的线程是否就是自己持有锁的线程ID也在对象头里如果是则正常往下执行。由于之前没有释放锁这里也就不需要重新加锁。如果自始至终使用锁的线程只有一个很明显偏向锁几乎没有额外开销性能极高。
偏向锁是指当一段同步代码一直被同一个线程所访问时即不存在多个线程的竞争时那么该线程在后续访问时便会自动获得锁从而降低获取锁带来的消耗即提高性能。
当一个线程访问同步代码块并获取锁时会在 Mark Word 里存储锁偏向的线程 ID。在线程进入和退出同步块时不再通过 CAS 操作来加锁和解锁而是检测 Mark Word 里是否存储着指向当前线程的偏向锁。轻量级锁的获取及释放依赖多次 CAS 原子指令而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令即可。
偏向锁只有遇到其他线程尝试竞争偏向锁时持有偏向锁的线程才会释放锁线程是不会主动释放偏向锁的。
轻量级锁
轻量级锁是指当锁是偏向锁的时候却被另外的线程所访问此时偏向锁就会升级为轻量级锁其他线程会通过自旋关于自旋的介绍见文末的形式尝试获取锁线程不会阻塞从而提高性能。
一旦有第二个线程加入锁竞争偏向锁就升级为轻量级锁自旋锁。这里要明确一下什么是锁竞争如果多个线程轮流获取一个锁但是每次获取锁的时候都很顺利没有发生阻塞那么就不存在锁竞争。只有当某线程尝试获取锁的时候发现该锁已经被占用只能等待其释放这才发生了锁竞争。
在轻量级锁状态下继续锁竞争没有抢到锁的线程将自旋即不停地循环判断锁是否能够被成功获取。获取锁的操作其实就是通过CAS修改对象头里的锁标志位。先比较当前锁标志位是否为“释放”如果是则将其设置为“锁定”比较并设置是原子性发生的。这就算抢到锁了然后线程将当前锁的持有者信息修改为自己。
长时间的自旋操作是非常消耗资源的一个线程持有锁其他线程就只能在原地空耗CPU执行不了任何有效的任务这种现象叫做忙等busy-waiting。如果多个线程用一个锁但是没有发生锁竞争或者发生了很轻微的锁竞争那么synchronized就用轻量级锁允许短时间的忙等现象。这是一种折衷的想法短时间的忙等换取线程在用户态和内核态之间切换的开销。
重量级锁
重量级锁显然此忙等是有限度的有个计数器记录自旋次数默认允许循环10次可以通过虚拟机参数更改。如果锁竞争情况严重某个达到最大自旋次数的线程会将轻量级锁升级为重量级锁依然是CAS修改锁标志位但不修改持有锁的线程ID。当后续线程尝试获取锁时发现被占用的锁是重量级锁则直接将自己挂起而不是忙等等待将来被唤醒。
重量级锁是指当有一个线程获取锁之后其余所有等待获取该锁的线程都会处于阻塞状态。
简言之就是所有的控制权都交给了操作系统由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换线程的挂起和唤醒从而消耗大量的系统资
synchronized与lock锁区别
总结如下
1️⃣ lock 是一个接口而 synchronized 是 Java 的一个关键字synchronized 是内置的语言实现。 2️⃣ 异常是否释放锁synchronized 在发生异常时候会自动释放占有的锁因此不会出现死锁而 lock 发生异常时候不会主动释放占有的锁必须手动 unlock 来释放锁可能引起死锁的发生。(所以最好将同步代码块用 try catch 包起来finally 中写入 unlock避免死锁的发生。) 3️⃣ 是否响应中断 lock 等待锁过程中可以用 interrupt 来中断等待而 synchronized 只能等待锁的释放不能响应中断。 4️⃣ 是否知道获取锁Lock 可以通过 trylock 来知道有没有获取锁而 synchronized 不能。 5️⃣ Lock 可以提高多个线程进行读操作的效率。(可以通过 ReadWriteLock 实现读写分离) 6️⃣ 在性能上来说如果竞争资源不激烈两者的性能是差不多的而当竞争资源非常激烈时(即有大量线程同时竞争)此时 Lock 的性能要远远优于 synchronized。所以说在具体使用时要根据适当情况选择。 7️⃣ synchronized 使用 Object 对象本身的 wait 、notify、notifyAll 调度机制而 Lock 可以使用 Condition 进行线程之间的调度。
性能区别
synchronized 和 lock 性能区别 synchronized 是托管给 JVM 执行的而 lock 是 Java 写的控制锁的代码。在 Java1.5 中synchronized 是性能低效的。因为这是一个重量级操作但是到了 Java1.6发生了变化,进行了很多优化有适应自旋轻量级锁偏向锁等等。 synchronized 原始采用的是 CPU悲观锁机制即线程获得的是排他锁。排他锁意味着其他线程只能依靠阻塞来等待线程释放锁。 而 Lock 用的是乐观锁机制。所谓乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作如果因为冲突失败就重试直到成功为止。乐观锁实现的机制就是 CAS 操作(Compare and Swap)。进一步研究 ReentrantLock 的源码会发现其中比较重要的获得锁的一个方法是 compareAndSetState。这里其实就是调用的 CPU 提供的特殊指令。
用途区别
synchronized 和 lock 用途区别 synchronized 原语和 ReentrantLock 在一般情况下没有什么区别但是在非常复杂的同步应用中请考虑使用 ReentrantLock 特别是遇到下面两种需求的时候。 1️⃣ 某个线程在等待一个锁的控制权的这段时间需要中断。 2️⃣ 分开处理一些 wait-notifyReentrantLock 里面的 Condition 应用能够控制 notify 哪个线程。 3️⃣ 公平锁功能每个到来的线程都将排队等候。
先说第一种情况ReentrantLock 的 lock 机制有 2 种忽略中断锁和响应中断锁这带来了很大的灵活性。比如如果 A、B 两个线程去竞争锁A 线程得到了锁B 线程等待但是 A 线程这个时候实在有太多事情要处理就是一直不返回B 线程可能就会等不及了想中断自己不再等待这个锁了转而处理其他事情。
这个时候 ReentrantLock 就提供了两种机制可中断/可不中断 ①B 线程中断自己(或者别的线程中断它)但是 ReentrantLock 不去响应继续让 B 线程等待你再怎么中断我全当耳边风(synchronized 原语就是如此) ②B 线程中断自己(或者别的线程中断它)ReentrantLock 处理了这个中断并且不再等待这个锁的到来完全放弃。
缓存三兄弟缓存击穿、穿透、雪崩
缓存击穿 缓存击穿redis中没有但是数据库有 顺序先查缓存判断缓存是否存在如果缓存存在直接返回数据如果缓存不存在則查询数据库将数据库的数据存入到缓存 解决方案将热点数据设置过期时间长一点针对数据库的热点访问方法上分布式锁
缓存穿透 缓存穿透redis中没有数据库也没有 解决方案
1将不存在的key在redis设置值为null
2使用布隆过滤器;
原理https://zhuanlan.zhihu.com/p/616911933 布隆过滤器
如果确认key不存在于redis中那么就一定不存在
它说key存在就有可能存在也可能不存在 误差 布隆过滤器 1、根据配置类中的 key的数量 误差率计算位图数组【二维数组】 2、通过布隆过滤器存放key的时候会计算出需要多少个hash函数由hash函数算出多少个位图位置需要设定为1 3、查询时根据对应的hash函数判断对应的位置值是否都为1如果有位置为0则表示key一定不存在于该redis服务器中如果全部位置都为1则表示key可能存在于redis服务器中 缓存雪崩 缓存雪崩 Redis的缓存雪崩是指当Redis中大量缓存数据同时失效或者被清空时大量的请求会直接打到数据库上导致数据库瞬时压力过大甚至宕机的情况。
造成缓存雪崩的原因主要有两个
1.相同的过期时间当Redis中大量的缓存数据设置相同的过期时间时这些数据很可能会在同一时间点同时失效导致大量请求直接打到数据库上。
2.缓存集中失效当服务器重启、网络故障等因素导致Redis服务不可用且缓存数据没有自动进行容错处理当服务恢复时大量的数据同时被重新加载到缓存中也会导致大量请求直接打到数据库上。
预防缓存雪崩的方法主要有以下几种
1.设置不同的过期时间可以将缓存数据的过期时间分散开避免大量缓存数据在同一时间点失效。
2.使用加锁可以将所有请求都先进行加锁操作当某个请求去查询数据库时如果还没有加载到缓存中则只让单个线程去执行加载操作其他线程等待该线程完成后再次进行判断避免瞬间都去访问数据库从而引起雪崩。
3.提前加载预热在系统低峰期可以提前将部分热点数据加载到缓存中这样可以避免在高峰期缓存数据失效时全部打到数据库上。
4.使用多级缓存可以在Redis缓存之上再使用一层缓存例如本地缓存等当Redis缓存失效时还能够从本地缓存中获取数据避免直接打到数据库上。 本地缓存ehcache oscache spring自带缓存 持久层框架的缓存 总结
Java进阶锁——锁的升级synchronized与lock锁区别