1.如何保证变量的可见性
使用volatile关键字,将变量声明为volatile,这就指示JVM,这个变量共享且不稳定,每次使用它都到主存中读取,该关键字还可以防止JVM指令重排序
2.乐观锁和悲观锁
2.1 乐观锁
2.1.1 概念
乐观锁总是假设最好的情况,认为共享资源每次访问不会出现问题,线程可以不停的执行,无需加锁也无需等待,只是在提交修改的时候验证对应的资源是否被其他线程修改了
2.1.2 实现方法
使用版本号机制:一般是在数据库表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,该字段值就+1,当线程A要更新数据时,读取原数据也会读取到version数据,在提交更新时,若刚才读到的version值为当前数据库的version值,就执行更新操作,否则就重试更新操作
使用CAS算法:CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令
2.2 悲观锁
2.2.1 概念
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候都会出现问题,所以每次在获取资源的时候都会上锁,这样其他线程想要拿到资源就会阻塞直到锁被上一个持有的线程释放,总体来讲就是共享资源每次都只给一个线程使用,其他线程阻塞,用完后才把资源转让给其他线程
2.2.2 实现方法
在使用资源时给资源上锁
2.3 两者使用场景
高并发场景下,乐观锁相比悲观锁来说,不存在锁竞争造成死锁问题,在性能上更胜一筹,但是,如果冲突频繁发生(写占比非常多的场景),会频繁失败和重试。也非常影响性能
1.悲观锁多用于写占比多的场景,避免频繁失败和重试,而悲观锁消耗的资源固定的,但是如果乐观锁解决了这个问题(比如LongAdder),就可以考虑使用乐观锁
2.乐观锁多用于读占比多的场景,避免频繁加锁浪费资源,不过乐观锁主要针对的对象是单个共享变量
3.synchronized关键字
3.1 概念
sunchronized是JAVA中的一个关键字,主要是为了解决多个线程之间访问资源的同步性,可以保证被它修饰的方法或代码块在任意时刻都只能被一个线程执行
3.2 使用方式
3.2.1 修饰实例方法(锁当前对象实例)
给当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
synchronized void method() {
// 业务代码
}
3.2.1 修饰静态方法(锁当前类)
给当前类加锁,会作用当类的所有对象实例,进入同步代码前要获得当前class的锁,因为静态成员不属于任何实例对象,归整个类所有,不依赖类的特定实例,被类的所有实例共享
synchronized static void method() {
// 业务代码
}
3.2.3 修饰代码块(锁指定对象/类)
对括号里指定的对象/类加锁
synchronized(this) {
// 业务代码
}