网站开发工程师简介,邯郸建设局网站,网网站建设公司,五星级酒店网站建设目录 引出对象原子更新AtomicReferenceAtomicLongFieldUpdaterABA问题 Redis冲冲冲——缓存三兄弟#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出
Java多线程——对象的原子更新 对象原子更新
AtomicReference
package cn.test3;
import lombok.AllAr… 目录 引出对象原子更新AtomicReferenceAtomicLongFieldUpdaterABA问题 Redis冲冲冲——缓存三兄弟缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出
Java多线程——对象的原子更新 对象原子更新
AtomicReference
package cn.test3;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
Data
NoArgsConstructor
AllArgsConstructor
class CartItem{private Long itemId;private Long num;
}
public class App17 {/*** AtomicReference* 1、原子更新引用类型* 2、AtomicReference是对对象进行原子操作保证多线程操作同一个对象时候的线程安全性*/public static void main(String[] args) throws InterruptedException {//1. 创建原子更新引用类型保证多线程操作对象的原子性AtomicReferenceCartItem atomicReference new AtomicReference();CartItem cartItem new CartItem(1L,100L);//2. 存入对象atomicReference.set(cartItem);//3. 原子方式更新对象会与set时候存入的对象进行对比如果是同一个对象更新成功返回trueboolean result atomicReference.compareAndSet(cartItem, new CartItem(2L, 200L));System.out.println(result result);System.out.println(atomicReference.get());}
}AtomicLongFieldUpdater
/*** AtomicLongFieldUpdater* 1、原子更新字段类型* 2、保证对象属性原子操作线程安全*/
public static void main(String[] args) throws InterruptedException {AtomicLongFieldUpdater atomicIntegerFieldUpdater AtomicLongFieldUpdater.newUpdater(CartItem.class,num);CartItem user new CartItem(1L,100L);boolean flag atomicIntegerFieldUpdater.compareAndSet(user, user.getNum(), 101);System.out.println(flag flag);System.out.println(原子更新后的值atomicIntegerFieldUpdater.get(user));
}要求字段必须式 public volatile long基本类型
ABA问题
ABA问题: 如果另一个线程修改V值假设原来是A先修改成B再修改回成A。当前线程的CAS操作无法分辨当前V值是否发生过变化。
危害
小明在提款机提取了50元因为提款机问题有两个线程同时把余额从100变为50 线程1提款机获取当前值100期望更新为50 线程2提款机获取当前值100期望更新为50 线程1成功执行线程2某种原因block了这时某人给小明汇款50 线程3默认获取当前值50期望更新为100 这时候线程3成功执行余额变为100 线程2从Block中恢复获取到的也是100compare之后继续更新余额为50 此时可以看到实际余额应该为100100-5050但是实际上变为了50100-5050-50这就是ABA问题带来的成功提交。
解决方法 在变量前面加上版本号每次变量更新的时候变量的版本号都1即A-B-A就变成了1A-2B-3A。
public static void main(String[] args) throws InterruptedException { AtomicStampedReferenceInteger asr new AtomicStampedReference(50,0);Thread thread1 new Thread(()-{asr.compareAndSet(50,100,asr.getStamp(),asr.getStamp()1);try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}asr.compareAndSet(100,50,asr.getStamp(),asr.getStamp()1);});Thread thread2 new Thread(()-{int stamp asr.getStamp();System.out.println(thread2获取版本 stamp);boolean result asr.compareAndSet(50, 200, stamp, asr.getStamp() 1);System.out.println(result result);},thread2);thread1.start();thread2.start();
}单独测试
public class App20 {public static void main(String[] args) throws InterruptedException {Integer num1 1000;Integer num2 2000;Integer num3 3000;Integer num4 4000;AtomicStampedReferenceInteger asr new AtomicStampedReference(num1, 0);boolean b3 asr.compareAndSet(num1, num2, asr.getStamp(), asr.getStamp()1);boolean b2 asr.compareAndSet(num2, num3, asr.getStamp(), asr.getStamp()1);boolean b asr.compareAndSet(num3, num4, asr.getStamp(), asr.getStamp()1);System.out.println(b b);System.out.println(asr.getReference());}
}Redis冲冲冲——缓存三兄弟缓存击穿、穿透、雪崩
缓存击穿 缓存击穿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多线程——对象的原子更新