Java高并发(面试必看)持续更新
2020-07-27 11:26:26 0 举报
一张图搞定java高并发
作者其他创作
大纲/内容
首先每个对象都有对象头,对象头中存储着Mark Word和Klass Word。其中Mark Word是用来存储hashcode、分代年龄、加锁状态等信息。而Klass Word是存储该对象所对应的Class。在使用synchronize语法后,会将对象的加锁状态改为10然后Mark Word会关联操作系统的Monitor。当线程执行synchronize里面的代码的时候,会将Monitor中的Owner改为当前线程,以标识当前锁已被占用。当其他线程再来访问锁中的内容的时候,会进入Monitor中的EntrySet等待队列等待。当占用锁的线程释放掉锁之后,会随机在等待队列中唤醒一个线程继续占用锁
与Object的wait和notify相比
interrupt
线程池
每个线程启动后,虚拟机都会为其开辟一块栈内存,每个栈里有多个栈祯组成,对应着每次方法调用时所占用的内存。当线程的CPU时间片用完的时候就会发生上下文切换,这时程序计数器便会记录当前线程执行到的位置。(每个线程都有自己的程序计数器)
什么是并行和并发
守护线程的特点就是等所有的非守护线程运行结束了,会强制结束守护线程。
在放入EntrySet前会有一个自旋优化
偏向锁(最优先使用)
单例设计模式(double-checked locking)
进程与线程
每个线程都有自己的parker对象,park里有三个变量分别表示状态、锁对象和阻塞队列。当使用park的时候,会先判断状态是否为0.如果是0的话,进入阻塞队列,然后再设置状态为0。当调用unpark方法的时候,先设置状态值为1,然后将阻塞队列中的线程回复运行,再将状态设为0
在重量级锁中,如果获得owner的线程执行了wait方法,就会将正在执行的线程放到waitSet(休息室)中去,然后再从EntrySet(等待队列中唤醒一个线程)。当正在执行的owner线程执行了notify或者是notifyAll时会唤醒waitSet中的线程
等待更新
创建线程的方法
什么是线程和进程
两个线程访问时
isInterrupted()可以获得线程的打断状态。使用interrupt方法后并不是立即打断线程,而是将打断状态改为true。注意:打断sleep、wait和join会清空打断状态,所以为false
共享模型之管程(Monitor)悲观锁
①可中断②可以设置超时时间③可以设置公平锁(先到先得,而不是随机)④支持多个条件变量(多个waitSet休息室)
①使用匿名内部类②使用Runnable接口③使用FutureTask,可以返回结果。
①wait是Object的方法,而sleep是Thread的方法②wait需要拿到锁之后才能使用,否者会报异常,sleep不用③sleep的过程中是不会释放锁的,而wait的过程释放锁
Java的一个关键字,在 JVM 内存模型中内存分为主内存和工作内存,各线程有独自的工作内存,对于要操作的数据会从主内存拷贝一份到工作内存中,默认情况下工作内存是相互独立的,也就是线程之间不可见。使用volatile关键字可以将变量实时的刷新回主存,保证了可见性。同时也保证了有序性,不会导致指令重排。
常见的方法
将线程的Id直接写到Object(加锁的对象)的Mark Word上面。这样进行锁重入的时候就避免了CAS操作。提升了性能。
park/unpark
加锁位置
①wait/notify必须配合synchronize使用而park不用②notify不能进行精准的唤醒,有可能导致虚假唤醒,而unpark可以精准的唤醒③park/unpark可以先进行unpark操作再进行park操作(这样会使后面的park操作无效)
sleep与yield
保证指令不会受 cpu 缓存的影响
保证指令不会受 cpu 指令并行优化的影响
Java内存模型(JMM)
解决方案:使用JUC下提供的AtomicStampedReference,他有一个相当于版本号的参数。可以查看修改过多少次
与synchronize相对比
LockSupport.park();方法同样也会使线程进入等待状态。如果这时候使用interrupt方法打断的话,后面的park方法就会失效。解决这个问题只需要在第二个park方法前使用Thread.interrupted()方法清除打断状态即可。
可以使用同步代码块,使用同步代码块的话就可以锁任意一个对象。当他应用在方法上的时候,锁的就为this。应用在静态方法,锁的就是它的class对象。(线程八锁练习题)
取消偏向锁的三种情况
有序性
当使用synchronize关键字时首先是使用轻量级锁进行加锁,在线程的栈空间中添加锁记录对象。锁记录中有两个变量,一个用来记录锁的那个obj的对象地址,另一个用来保存对象的Mark Word。在拷贝对象的MarkWord到栈祯中去的时候(cas操作),如果当前对象已经是加锁的状态分两种情况:①其它线程获取了锁,这时候进入锁膨胀的过程。②如果此线程已经获取了锁,会再加一条栈祯记录,以表示加锁的次数。解锁阶段:将栈祯中刚才拷贝的对象的MarkWord拷贝回原来的对象中。如果失败,说明发生了锁膨胀,或已经升级成了重量级锁
保证指令不会受到线程上下文切换的影响
原子引用: AtomicReference AtomicMarkableReference AtomicStampedReference
初始状态、可运行状态、运行状态、阻塞状态、终止状态
可见性
CAS操作(Compare And Swap)
wait/notify
JUC下提供的工具类
synchronize
Java线程
缺点:①在高并发的情况下,因为有一个while循环会一直进行比较,给CPU带来了很大的压力。②会发生ABA问题(之前读和过一段时间读在这之间有第三个人修改过,但是他又修改过来了,这样检测不到)
线程运行的原理
原理
轻量级锁(优先与重量级锁)
守护线程
调用wait/notify
join
原子整数:AtomicBooleanAtomicInteger AtomicLong
wait和sleep的区别
volite关键字
调用hashCode
ReentrantLock
原理:线程拿到的最新值与内存中的值作比较,如果不一致返回false
synchronize原理
线程的运行状态
start与run
重量级锁(获取操作系统中的Monitor)
当有一个线程对对象增加了轻量级锁,另一个线程又对该对象加锁的时候,就会发生锁膨胀。因为轻量级锁是没有阻塞状态的,所以这是会对锁进行升级成为重量级锁。将Object对象中Mark Word改为重量级锁的地址。当获取轻量级锁的那个线程执行结束,准备将Mark Word拷贝回Object对象的时候会发生错误。因为现在Object已经是重量级锁了,会走重量级锁的解锁流程。将owner置为null,再从EntrySet中唤醒一个线程。
乐观锁(无锁)
原子性
锁膨胀(轻量级锁升级为重量级锁)
0 条评论
回复 删除
下一页