JUC并发编程学习笔记 (9)

指令重排

什么是指令重排?

我们写的代码逻辑,先后顺序,计算机并不是按照代码的顺序去执行的,计算机会对我们写的代码进行指令重排,优化执行效率和速度。

源代码--》编译器优化的重排--》指令并行也可能会重排--》内存系统也会重排--》最后才到执行

处理器在进行指令重排时会考虑数据之间依赖性,即happen-before原则。

volatile避免指令重排是通过内存屏障实现的。

CPU内存屏障的作用:

保证了特定操作的执行顺序。

可以保证某些操作的内存可见性。

CAS

比较并替换。比较当前工作内存中的值和主内存中的值,如果这个值是期望的,也就是相等的,那么就执行替换操作,否则就一直循环自旋。

原子类进行CAS操作是自旋锁,当比较失败后会重新比较,知道成功为止才跳出循环。

缺点

循环会耗时

一次只能保证一个共享变量的原子性

ABA问题

cas1.png

Unsafe类

Unsafe类的出现是因为Java无法操作内存,这时候Java类想要操作内存是通过本地方法,调用C++的代码来操作内存,因为C++代码可以操作内存。Unsafe类相当于Java的后门。比较并替换时是获取内存地址中的值,这种直接内存操作效率高。

ABA问题——通过乐观锁解决 代码示例 public class ABATest { public static void main(String[] args) { /** * 注意AtomicStampedReference<Integer>泛型类型比较,如果是包装类,则要注意是否在缓存-128——127之间,超过这个 * 范围的值就不是读取缓存,而是在堆空间中直接创建一个包装类对象,这时候compareAndSet方法里是用==比较,比较的是地址, * 即是否为同一个对象,会由此导致错误无法执行成功。 */ //// 正常在业务操作,这里面比较的都是一个个对象 AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<Integer>(1,1); new Thread(()->{ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(stampedReference.compareAndSet(1, 2, stampedReference.getStamp(), stampedReference.getStamp() + 1)); System.out.println("A stamp"+stampedReference.getStamp()); System.out.println(stampedReference.compareAndSet(2, 1, stampedReference.getStamp(), stampedReference.getStamp() + 1)); System.out.println("A stamp"+stampedReference.getStamp()); },"A").start(); new Thread(()->{ int stamp = stampedReference.getStamp(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(stampedReference.compareAndSet(1, 21111111, stamp, stamp + 1)); System.out.println("B stamp"+stampedReference.getStamp()); },"B").start(); } /** * true * A stamp2 * true * A stamp3 * false * B stamp3 */ }

注意比较并替换源码是用==比较。

自旋锁 自定义锁,通过自旋实现锁同步——代码示例 public class SpinLockTest { public static void main(String[] args) { MySpinLock lock = new MySpinLock(); new Thread(()->{ lock.lock(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } },"T1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ lock.lock(); try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } },"T2").start(); } /** * T1 lock * T2 lock * T1 unlock * T2 unlock */ } class MySpinLock { private AtomicReference<Thread> reference = new AtomicReference<>(); public void lock() { System.out.println(Thread.currentThread().getName() + " lock"); while (!reference.compareAndSet(null, Thread.currentThread())) { } } public void unlock() { reference.compareAndSet(Thread.currentThread(), null); System.out.println(Thread.currentThread().getName() + " unlock"); } } 公平锁、非公平锁

Lock类通过传参可以实现公平锁、非公平锁。

可重入锁 死锁 //查看进程号 jps -l //jstack查看线程情况 进程号 jstack pid 扩展 单例模式

枚举自带单例模式,当我们使用反射去破坏单例时,会抛出异常:IllegalArgumentException("Cannot reflectively create enum objects");

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxsxy.html