JUC-ReentrantLock,ReentrantReadWriteLock,StampedLock
1. 概述前面介绍过了synchronized关键字作用的锁升级过程无锁->偏向锁->轻量锁->重锁下面再介绍实现Lock接口的锁的升级过程无锁->独占锁(ReentrantLock,Synchronized)->读写锁(ReentranReadWriteLock)->邮戳锁(StampedLock)并准备了一些问题,回顾一下自己对知识的掌握程度。
你知道Java里面有哪些锁?
你说你用过读写锁,锁饥饿问题是什么?有没有比读写锁更快的锁?
StampedLock知道吗?(邮戳锁/票据锁)
ReentrantReadWriteLock有锁降级机制,你知道吗?
2. ReentrantLockReentantLock是可重入的独占锁。默认是非公平锁。可重入:当一个线程持有锁后,在内部可以继续获取锁。独占:是一种悲观锁,当一个线程持有锁的时候,其他线程会阻塞。公平和非公平:在公平的机制下,线程会依次排队,放到等待队列中。排队获取锁。在非公平的机制下,新来的线程通过CAS获取锁,获取不到,才会进入等待队列。
2.1 ReentrantLo ...
JUC-synchronized无锁、偏向锁、轻量级锁、重量级锁
1 synchronized实操
关键字synchronized可以用来保证多线程并发安全的原子性、可见、有序性。关键字synchronized不仅可以作用于方法还可以作用于同步代码块,能够保证作用范围中线程访问安全。
注意:局部变量是线程安全的。线程不安全问题只存在于实例变量。
synchronized关键字作用如下图:
同步方法:分为静态方法和非静态方法,对静态方法,锁作用对象是类对象。对非静态方法,锁作用对象是实例对象。
同步方法块:通过synchronized(obj)来对某个对象进行加锁。如果是this表示实例对象。如果是xx.class表示作用于类对象。
1.1 锁作用于类对象和实例对象的区别?
锁作用于实例对象:当多个线程访问同一个实例对象的同步块的时候,存在竞争关系。只能有一个线程能访问当前实例对象的锁。其他线程只能等待,直到占所有的线程释放实例对象的锁。
锁作用于类对象:当多个线程访问同一个类的不同实例对象的同步块的时候,存在竞争关系。因为锁所用于类上,虽然是不同的实例对象但是所属同一个类,只有一把类锁。因此多个线程仍然存在竞争关系。
举例1234567 ...
JUC-Java对象内存布局(对象头,类型指针,自填充)
1. 引入问题有如下问题:
请解释一下对象的创建过程
Class对象在堆还是在方法区中
对象在内存中的存储布局
对象头具体包括什么
对象怎么定位
对象怎么分配
Object obj = new Object(); 在内存中占用多少个字节
2. 堆内存布局2.1 基于分代的堆的内存布局(复习)
堆:
年轻代(1)
伊甸园区(8)
s0(1)
s1(1)
老年代(2)(注:元空间在堆外内存中)
2.2 对象在堆中组成部分
对象在堆中主要由下面三个部分组成
对象头(Header)
实例数据(Instance Data)
对齐填充(Padding)
其中对象头又分为对象标志(存储对象的基本信息)和类型指针(指向类模板)。
2.3 对象头2.3.1 对象标志(Mark Word)对象标志用8字节64位来存储类的一些基本信息。比如:hashcode,GC分代年龄,锁标志等注意这里分代年龄是4位。联想之前S区的分代年龄到达了15还没被GC就会到老年区,这里为什么是15?因为底层给了4bit来计算分代年龄。这里的15 = 2 ^ 4 - 1
2.3.2 类型指 ...
JUC-原子操作类(AtomicLong, AtomicLongArray,AtomicReferren,AtomicLongFiledUpdater)LongAdder
1. 概述多线程下使用原子类来保证线程安全
12345678910111213public class AtomicIntegerTest { AtomicInteger atomicInteger = new AtomicInteger(0); public int getNum(){ return atomicInteger.get(); } public void addNum(){ atomicInteger.getAndIncrement(); }}
volatile能解决多线程并发安全中的可见性问题,但是不能解决原子性问题。atomic原子类通过CAS+volatile来保证并发安全。CAS保证原子性,volatile保证原子性。
2. 分类原子类位于java.util.concurrent.atomic包下面。可以分为以下几类,基本类型的原子类,数组类型的原子类,引用类型的原子类和对象的属性修改原子类四类。
2.1 基本类型原子类AtomicBo ...
JUC-CAS
1. CAS概述CAS(Compare ans swap/set) 比较并交换,实现并发的一种底层技术。它将预期的值和内存中的值比较,如果相同,就更新内存中的值。如果不匹配,一直重试(自旋)。Java.util.concurrent.atomic包下的原子类都使用了CAS算法
2. CAS原理CAS具体的操作是将预期的值和内存中真实的值进行比较,如果相同就更新值。如果不相同就重试(自旋)。CAS是通过Unsafe的compareAndSwap方法实现的,底层实现是CPU原子指令cmpxchg,不会造成数据不一致的问题。CAS依靠底层硬件实现的无锁原子算法。比synchronized重量级锁性能更好。
3. CAS与自旋锁3.1 前置知识:原子引用类AtomicReference将自定义的类型变成原子类,能够进行cas操作。
123456789public static void main(String[] args){ AtomicReference<User> atomicReference = new AtomicReference< ...
JUC-JMM与volatile
volatile简介及作用volatile是JVM提供的轻量级的同步机制。volatile关键字能够保证并发编程的三大特性中的可见性,有序性。但是不能保证原子性。保证可见性:经过volatile修饰的变量,在本地内存中修改之后,会立即刷回主内存中。当主内存中的共享变量修改之后,其他线程的本地内存会立即同步获取到这个最新的值。保证有序性:通过禁止指令重排优化来保证有序性。禁止指令重排优化是通过内存屏障来实现的。
内存屏障内存屏障就是CPU或者编译器对内存随机访问的一个同步点,使得对该点之前的操作在该点之后的操作之前。避免代码重排序。内存屏障粗分类:
读屏障:在读操作之前插入读屏障,让工作内存中的缓存失效,进而从主存中获取最新的数据。
写屏障:在写操作之后插入写屏障,让工作内存中的缓存立即写入到主内存中去。
内存屏障细分类:LoadLoad: 保证load1在load2之前执行。StoreStore:保证store1保存在store2保存之前。LoadStore:保证load1读取数据在store1写入数据之前。StoreLoad:保证store保存数据在load读取数据之前。
...
JUC-Java内存模型JMM
JMM概述Java Meory Model java内存模型。在不同的硬件和不同的操作系统上,对内存的访问方式是不一样的。这就造成了同一套java代码运行在不同的操作系统上会出问题。JMM就屏蔽掉硬件和操作系统的差异,增加java代码的可移植性。这是一方面。另一方面JMM定义的一系列规则能够保证线程并发的安全性。主要是保证线程的可见性,有序性,原子性。具体来说它主要就是抽象了线程和主存之间的关系。(如下图)每个线程都有一个工作内存(cpu缓存),工作内存中存放着主存(内存)的副本,一般是共享变量,比如实例变量,静态变量但是不包括局部变量。线程读写数据是直接操作工作内存的。线程不能访问其他线程的工作内存。多个线程间通信是通过主存来完成的。
线程三大问题原子性,可见性,有序性。(简记:客源有)线程需要满足这三大特性,才能保证线程并发安全。而JMM就是为了实现这三大特性定义的一系列规则。
可见性问题可见性指的是当一个线程修改了共享变量后,另一个线程能够立马得到修改的这个值。但是由于CPU缓存的存在,可见性往往会存在一些问题。比如说在多线程下,每个线程将变量存放在CPU缓存中,一个线程修 ...
JUC-中断机制和LockSupport
线程中断机制概念java提供了一种用于停止线程的协商机制-中断。称为中断标识协商机制。
常用API
public void interrupt()仅仅让线程的中断标志位设置为true。不进行其他操作。
public boolean isInterrupted()获取中断标志位的状态。
public static boolean interrupted()获取中断标志位的状态。并将中断标志位设置为false
如何停止中断运行的线程volatile变量实现12345678910111213141516171819private static volatile boolean isStop = false; public static void main(String[] args) { new Thread(() -> { while (true) { if (isStop) { System.out.println(Thread.c ...
JUC-锁
乐观锁和悲观锁悲观锁当一个线程在操作资源的时候,会悲观的认为有其他的线程会来抢占该资源,因此会在操作资源前进行加锁,避免其他线程抢占。Synchronized关键字和Lock实现类就是悲观锁。显示的锁定资源后再对资源进行操作。使用场景:
适合写操作多的场景。先加锁能够保证写操作时数据正确
本质:加锁去操作同步资源。
乐观锁当一个线程去操作资源的时候,会乐观的任务其他线程不会来抢占资源,因此不会加锁。java中通过无锁编程来实现,只是在对数据进行修改的时候,判断其他线程是否对该数据进行修改过
如果没有修改过,该线程直接修改数据。
如果修改过,该线程则根据不同的实现方式执行不同的操作,比如放弃修改,重试抢锁等等。
原子操作类那些底层的是CAS(Compare And swap)算法,也就是乐观锁。判断规则:
版本号机制Version(每修改一次版本号递增,当前版本号是最大的,可以直接修改。不是最大的,意味着别人修改过了,我的修改要重新处理)
最常采用的是CAS算法(后面会详细讲,这里略)
使用场景:乐观锁适合读操作多的场景,不加锁读操作性能大幅提升本质:无锁去操作同步资源 ...
JUC-CompletableFuture
Future接口理论Future接口定义了异步任务执行的一些方法,包括异步任务执行结果,异步任务执行是否中断,异步任务是否完毕等。
Future接口常用实现类FutureTask异步任务1234567FutureTask<String> futureTask = new FutureTask<String>( () -> { System.out.println(Thread.currentThread().getName()+"\t -----come in"); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } return "task over";});Thread t1 = new Thread(futureTask, "t1");t1.start();
Completable ...