最后
如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!
JVM中的内存屏障
=========
所有实现JVM规范的虚拟机,必须实现四个屏障
LoadLoadBarrier LoadStore SL SS
volatile的底层实现
=============
volatile修饰的内存,不可以重排序,对volatile修饰变量的读写访问,都不可以换顺序
1: volatile i
2: ACC_VOLATILE
3: JVM的内存屏障
屏障两边的指令不可以重排!保障有序!
happends-before
as - if - serial
4:hotspot实现
bytecodeinterpreter.cpp
int field_offset = cache->f2_as_index();
if (cache->is_volatile()) {
if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
OrderAccess::fence();
}
orderaccess_linux_x86.inline.hpp
inline void OrderAccess::fence() {
if (os::is_MP()) {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
asm volatile (“lock; addl $0,0(%%rsp)” : : : “cc”, “memory”);
#else
asm volatile (“lock; addl $0,0(%%esp)” : : : “cc”, “memory”);
#endif
}
}
**LOCK 用于在多处理器中执行指令时对共享内存的独占使用。
它的作用是能够将当前处理器对应缓存的内容刷新到内存,并使其他处理器对应的缓存失效。**
另外还提供了有序的指令无法越过这个内存屏障的作用。
面试题
===
DCL单例要不要加volatile?(难)
总线/缓存锁
======
lock;
荐书 黄俊老师的书《深入理解JAVA并发原理》(出版中)
============================
操作系统的并发控制
=========
信号量与P-V原语
=========
互斥量
===
自旋锁
===
读写锁
===
中断控制与内核抢占
=========
顺序锁
===
rcu锁
====
JAVA启动线程的5种方法
=============
-
new MyThread().start()
-
new Thread®.start()
-
new Thread(lamda).start()
-
ThreadPool
-
Future Callable and FutureTask
常见的线程方法
=======
我们来认识几个线程的方法
sleep() yield() join()
package com.mashibing.juc.c_000;
public class T03_Sleep_Yield_Join {
public static void main(String[] args) {
//testSleep();
//testYield();
testJoin();
}
/Sleep,意思就是睡眠,当前线程暂停一段时间让给别的线程去运行。Sleep是怎么复活的?由你的睡眠时间而定,等睡眠到规定的时间自动复活/
static void testSleep() {
new Thread(()->{
for(int i=0; i<100; i++) {
System.out.println(“A” + i);
try {
Thread.sleep(500);
//TimeUnit.Milliseconds.sleep(500)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
/Yield,就是当前线程正在执行的时候停止下来进入等待队列(就绪状态,CPU依然有可能把这个线程拿出来运行),回到等待队列里在系统的调度算法里头呢还是依然有可能把你刚回去的这个线程拿回来继续执行,当然,更大的可能性是把原来等待的那些拿出一个来执行,所以yield的意思是我让出一下CPU,后面你们能不能抢到那我不管/
static void testYield() {
new Thread(()->{
for(int i=0; i<100; i++) {
System.out.println(“A” + i);
if(i%10 == 0) Thread.yield();
}
}).start();
new Thread(()->{
for(int i=0; i<100; i++) {
System.out.println(“------------B” + i);
if(i%10 == 0) Thread.yield();
}
}).start();
}
/*join, 意思就是在自己当前线程加入你调用Join的线程(),本线程等待。等调用的线程运行完了,自己再去执行。t1和t2两个线程,在t1的某个点上调用了t2.join,它会跑到t2去运行,t1等待t2运行完毕继续t1运行(自己join自己没有意义) */
static void testJoin() {
Thread t1 = new Thread(()->{
for(int i=0; i<100; i++) {
System.out.println(“A” + i);
try {
Thread.sleep(500);
//TimeUnit.Milliseconds.sleep(500)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(()->{
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0; i<100; i++) {
System.out.println(“A” + i);
try {
Thread.sleep(500);
//TimeUnit.Milliseconds.sleep(500)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
}
}
线程状态
====
小节说明:
-
本节重要程度:中 (帮助理解线程问题,保障知识完整性,面试很少考)
-
本节难度:低
JAVA的6中线程状态:
-
NEW : 线程刚刚创建,还没有启动
-
RUNNABLE : 可运行状态,由线程调度器可以安排执行
-
包括READY和RUNNING两种细分状态
-
WAITING: 等待被唤醒
-
TIMED WAITING: 隔一段时间后自动唤醒
-
BLOCKED: 被阻塞,正在等待锁
-
TERMINATED: 线程结束
如下图:
线程状态测试代码:
package com.mashibing.juc.c_000_threadbasic;
import com.mashibing.util.SleepHelper;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
-
title:${file_name}
-
关于线程状态的实验
-
@author 马士兵 http://www.mashibing.com
-
@date ${date}
-
@version 2.0
*/
public class T04_ThreadState {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("2: " + this.getState());
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(i + " ");
}
System.out.println();
}
}
public static void main(String[] args) throws Exception {
Thread t1 = new MyThread();
System.out.println("1: " + t1.getState());
t1.start();
t1.join();
System.out.println("3: " + t1.getState());
Thread t2 = new Thread(() -> {
try {
LockSupport.park();
System.out.println(“t2 go on!”);
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("4: " + t2.getState());
LockSupport.unpark(t2);
TimeUnit.SECONDS.sleep(1);
System.out.println("5: " + t2.getState());
final Object o = new Object();
Thread t3 = new Thread(()->{
synchronized (o) {
System.out.println(“t3 得到了锁 o”);
}
});
new Thread(()-> {
synchronized (o) {
SleepHelper.sleepSeconds(5);
}
}).start();
SleepHelper.sleepSeconds(1);
t3.start();
SleepHelper.sleepSeconds(1);
System.out.println("6: " + t3.getState());
}
}
线程的打断
=====
小节说明:
重要程度:中(面试不多)
小节难度:低
interrupt相关的三个方法:
=================
//Thread.java
public void interrupt() //t.interrupt() 打断t线程(设置t线程某给标志位f=true,并不是打断线程的运行)
public boolean isInterrupted() //t.isInterrupted() 查询打断标志位是否被设置(是不是曾经被打断过)
public static boolean interrupted()//Thread.interrupted() 查看“当前”线程是否被打断,如果被打断,恢复标志位
-
interrupt() :实例方法,设置线程中断标志(打扰一下,你该处理一下中断)
-
isInterrupted():实例方法,有没有人打扰我?
-
interrupted():静态方法,有没有人打扰我(当前线程)?复位!
interrupt和sleep() wait() join()
===============================
sleep()方法在睡眠的时候,不到时间是没有办法叫醒的,这个时候可以用interrupt设置标志位,然后呢必须得catch InterruptedException来进行处理,决定继续睡或者是别的逻辑,(自动进行中断标志复位)
interrupt是否能中断正在竞争锁的线程
======================
package com.mashibing.juc.c_000_threadbasic;
import com.mashibing.util.SleepHelper;
/**
- interrupt与sleep() wait() join()
*/
public class T09_Interrupt_and_sync {
private static Object o = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(()-> {
synchronized (o) {
SleepHelper.sleepSeconds(10);
}
});
t1.start();
SleepHelper.sleepSeconds(1);
Thread t2 = new Thread(()-> {
synchronized (o) {
}
System.out.println(“t2 end!”);
});
t2.start();
t2.interrupt();
}
}
interrupt()不能打断正在竞争锁的线程synchronized()
如果想打断正在竞争锁的线程,使用ReentrantLock的lockInterruptibly()
=================================================
package com.mashibing.juc.c_000_threadbasic;
import com.mashibing.util.SleepHelper;
import java.util.concurrent.locks.ReentrantLock;
/**
- interrupt与lockInterruptibly()
*/
public class T11_Interrupt_and_lockInterruptibly {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(()-> {
lock.lock();
try {
SleepHelper.sleepSeconds(10);
} finally {
lock.unlock();
}
System.out.println(“t1 end!”);
});
t1.start();
SleepHelper.sleepSeconds(1);
Thread t2 = new Thread(()-> {
System.out.println(“t2 start!”);
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println(“t2 end!”);
});
t2.start();
SleepHelper.sleepSeconds(1);
t2.interrupt();
}
}
优雅的结束线程
=======
小节说明:
本节内容的重要程度:中(面试有可能被问)
小节难度:低
结束线程的方法:
-
自然结束(能自然结束就尽量自然结束)
-
stop() suspend() resume()
-
volatile标志
-
不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
-
打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,
但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
- interrupt() and isInterrupted(比较优雅)
线程组
===
(不重要,暂忽略。)
ThreadGroups - Thread groups are best viewed as an unsuccessful experiment , and you may simply ignore their existence! - Joshua Bloch one of JDK designers
并发编程的特性
=======
可见性
===
有序性
===
原子性
===
线程的原子性
======
从一个简单的小程序谈起:
package com.mashibing.juc.c_001_sync_basics;
import java.util.concurrent.CountDownLatch;
public class T00_IPlusPlus {
private static long n = 0L;
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10000; j++) {
//synchronized (T00_IPlusPlus.class) {
n++;
//}
}
latch.countDown();
});
}
for (Thread t : threads) {
t.start();
}
latch.await();
System.out.println(n);
}
}
一些基本概念
race condition => 竞争条件 , 指的是多个线程访问共享数据的时候产生竞争
数据的不一致(unconsistency),并发访问之下产生的不期望出现的结果
如何保障数据一致呢?–> 线程同步(线程执行的顺序安排好),
monitor (管程) —> 锁
critical section -> 临界区
如果临界区执行时间长,语句多,叫做 锁的粒度比较粗,反之,就是锁的粒度比较细
具体: 保障操作的原子性(Atomicity)
-
悲观锁:悲观的认为这个操作会被别的线程打断(悲观锁)synchronized(上一个小程序)
-
乐观锁:乐观的认为这个做不会被别的线程打断(乐观锁 自旋锁 无锁)cas操作
CAS = Compare And Set/Swap/Exchange_/** * 解决同样的问题的更高效的方法,使用AtomXXX类 * AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的 * @author mashibing */_ packagecom.mashibing.juc.c_018_00_AtomicXXX; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class T01_AtomicInteger { /*volatile*/ _//int count1 = 0;_AtomicInteger count = new AtomicInteger(0); /* synchronized */void****m() { for (int i = 0; i < 10000; i++) _//if count1.get() < 1000_count.incrementAndGet(); //count1++ } public static void****main(String[] args) { T01_AtomicInteger t = new T01_AtomicInteger(); List threads = new ArrayList(); for (int i = 0; i < 100; i++) { threads.add(new Thread(t::m, “thread-” + i)); } threads.forEach((o) -> o.start()); threads.forEach((o) -> { try { o.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(t.count); } }
我们平时所说的"上锁",一般指的是悲观锁
上锁的本质
=====
上锁的本质是把并发编程序列化
package com.mashibing.juc.c_001_sync_basics;
import com.mashibing.util.SleepHelper;
public class T00_01_WhatIsLock {
private static Object o = new Object();
public static void main(String[] args) {
Runnable r = () -> {
//synchronized (o) { //打开注释试试看,对比结果
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
//}
};
for (int i = 0; i < 3; i++) {
new Thread®.start();
}
}
}
同时保障可见性
注意序列化并非其他程序一直没机会执行,而是有可能会被调度,但是抢不到锁,又回到Blocked或者Waiting状态(sync锁升级)
一定是锁定同一把锁(抢一个坑位)
package com.mashibing.juc.c_001_sync_basics;
import com.mashibing.util.SleepHelper;
public class T00_02_SingleLockVSMultiLock {
private static Object o1 = new Object();
private static Object o2 = new Object();
private static Object o3 = new Object();
public static void main(String[] args) {
Runnable r1 = () -> {
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
Runnable r2 = () -> {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
Runnable r3 = () -> {
synchronized (o3) {
System.out.println(Thread.currentThread().getName() + " start!");
SleepHelper.sleepSeconds(2);
System.out.println(Thread.currentThread().getName() + " end!");
}
};
new Thread(r1).start();
new Thread(r2).start();
new Thread(r3).start();
}
}
什么样的语句(指令)具备原子性?
================
CPU级别汇编,需要查询汇编手册!
Java中的8大原子操作:(了解即可,无需背过)
-
lock:主内存,标识变量为线程独占
-
unlock:主内存,解锁线程独占变量
-
read:主内存,读取内存到线程缓存(工作内存)
-
load:工作内存,read后的值放入线程本地变量副本
-
use:工作内存,传值给执行引擎
-
assign:工作内存,执行引擎结果赋值给线程本地变量
-
store:工作内存,存值到主内存给write备用
-
write:主内存,写变量值
JVM中的两中锁
========
重量级锁(经过操作系统的调度)synchronized早期都是这种锁(目前的实现中升级到最后也是这种锁)
轻量级锁(CAS的实现,不经过OS调度)(无锁 - 自旋锁 - 乐观锁)
CAS深度剖析
=======
CAS的ABA问题解决方案 - Version
CAS操作本身的原子性保障
AtomicInteger:
public final int incrementAndGet() {
for (;😉 {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Unsafe:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
运用:
package com.mashibing.jol;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class T02_TestUnsafe {
int i = 0;
private static T02_TestUnsafe t = new T02_TestUnsafe();
public static void main(String[] args) throws Exception {
//Unsafe unsafe = Unsafe.getUnsafe();
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
Field f = T02_TestUnsafe.class.getDeclaredField(“i”);
long offset = unsafe.objectFieldOffset(f);
System.out.println(offset);
boolean success = unsafe.compareAndSwapInt(t, offset, 0, 1);
System.out.println(success);
System.out.println(t.i);
//unsafe.compareAndSwapInt()
}
}
jdk8u: unsafe.cpp:
cmpxchg = compare and exchange set swap
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper(“Unsafe_CompareAndSwapInt”);
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
jdk8u:
atomic_linux_x86.inline.hpp 93行
is_MP = Multi Processors
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
int mp = os::is_MP();
-
asm volatile (LOCK_IF_MP(%4) “cmpxchgl %1,(%3)”
-
“=a” (exchange_value)
-
“r” (exchange_value), “a” (compare_value), “r” (dest), “r” (mp)
-
“cc”, “memory”);
return exchange_value;
}
jdk8u: os.hpp is_MP()
static inline bool is_MP() {
// During bootstrap if _processor_count is not yet initialized
// we claim to be MP as that is safest. If any platform has a
// stub generator that might be triggered in this phase and for
// which being declared MP when in fact not, is a problem - then
// the bootstrap routine for the stub generator needs to check
// the processor count directly and leave the bootstrap routine
// in place until called after initialization has ocurred.
return (_processor_count != 1) || AssumeMP;
}
jdk8u: atomic_linux_x86.inline.hpp
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
最终实现:
cmpxchg = cas修改变量值
lock cmpxchg 指令
硬件:
lock指令在执行的时候视情况采用缓存锁或者总线锁
两种锁的效率
======
不同的场景:
临界区执行时间比较长 , 等的人很多 -> 重量级
时间短,等的人少 -> 自旋锁
synchronized如何保障可见性
===================
JVM中的线程和OS线程对应关系
================
JVM 1:1 -> LOOM -> M:N (golang)
synchronized锁升级过程
=================
(内容太多,见另一篇文档)
常见的锁类型
======
-
悲观锁 乐观锁
-
自旋锁(CAS)
-
读写锁
-
排他锁 共享锁
-
分段锁
-
死锁 活锁
-
数据库的行锁 表锁 间隙锁 …
-
偏向锁
-
可重入锁
题外话:有没有程序天生就是线程安全的?
===================
有没有一门编程语言天生安全,目前有一门RUST,但是由于语言难度较大,同时缺乏强有力的团队推广,目前并不是很流行,对RUST有了解兴趣的,参考马士兵老师《RUST》
一些大厂难题
======
线程唤醒问题(阿里)
==========
样例代码
====
public class Test {
/**
-
有三个线程 A,B,C
-
A为什么总是在C前面抢到锁???
*/
private final static Object LOCK = new Object();
public void startThreadA() {
new Thread(() -> {
synchronized (LOCK) {
System.out.println(Thread.currentThread().getName() + “: get lock”);
//启动线程b
startThreadB();
System.out.println(Thread.currentThread().getName() + “: start wait”);
try {
//线程a wait
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “: get lock after wait”);
System.out.println(Thread.currentThread().getName() + “: release lock”);
}
}, “thread-A”).start();
}
private void startThreadB() {
new Thread(() -> {
synchronized (LOCK) {
System.out.println(Thread.currentThread().getName() + “: get lock”);
//启动线程c
startThreadC();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “: start notify”);
//线程b唤醒其他线程
LOCK.notify();
System.out.println(Thread.currentThread().getName() + “: release lock”);
}
}, “thread-B”).start();
}
private void startThreadC() {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + “: thread c start”);
synchronized (LOCK) {
System.out.println(Thread.currentThread().getName() + “: get lock”);
System.out.println(Thread.currentThread().getName() + “: release lock”);
}
}, “thread-C”).start();
}
public static void main(String[] args) {
new Test().startThreadA();
}
}
输出结果:
=====
thread-A: get lock
thread-A: start wait
thread-B: get lock
thread-C: thread c start
thread-B: start notify
thread-B: release lock
thread-A: get lock after wait
thread-A: release lock
thread-C: get lock
thread-C: release lock
问题:
===
为什么每次运行,线程A总是优先于线程C获取锁
分析:
===
在Hotspot源码中,我们知道synchronized关键字是通过monitor_enter和monitor_exit字节来实现的,最终用于阻塞线程的对象为ObjectMonitor对象,该对象包含三个关键字段:*WaitSet、*cxq、*EntryList。*WaitSet用于保存使用wait方法释放获得的synchronized锁对象的线程,也即我们调用wait函数,那么当前线程将会释放锁,并将自身放入等待集中。而cxq队列用于存放竞争ObjectMonitor锁对象失败的线程,而_EntryList用于也用于存放竞争锁失败的线程。那么它们之间有何区别呢?这是由于我们需要频繁的释放和获取锁,当我们获取锁失败那么将需要把线程放入竞争列表中,当唤醒时需要从竞争列表中获取线程唤醒获取锁,而如果我们只用一个列表来完成这件事,那么将会导致锁争用导致CPU资源浪费且影响性能,这时我们独立出两个列表,其中cxq列表用于竞争放入线程,而entrylist用于单线程唤醒操作。具体策略是这样的:
-
线程竞争锁失败后CAS放入cxq列表中
-
线程释放锁后将根据策略来唤醒cxq或者entrylist中的线程(我们这里只讨论默认策略)
-
默认策略下优先唤醒entrylist列表中的线程,因为唤醒线程对象的操作是单线程的,也即只有获取锁并且释放锁的线程可以操作,所以操作entrylist是线程安全的
-
如果entrylist列表为空,那么将会CAS将cxq中的等待线程一次性获取到entrylist中并开始逐个唤醒
在hotspot中我们称这种算法为电梯算法,也即将需要唤醒的线程一次性从竞争队列中放入entrylist唤醒队列。
那么这时我们就可以分析以上代码为何总是唤醒线程A了,我们先看线程执行顺序,首先启动线程A,随后线程A启动线程B,B线程需要获取对象锁从而创建线程C,我们看到当线程A调用wait方法将自己放入等待集中后,将会唤醒线程B,随后线程B创建并启动了线程C,然后等待C开始执行,由于此时对象锁由线程B持有,所以线程C需要放入cxq竞争队列,随后B从睡眠中醒来,执行notify方法,该方法总是唤醒了线程A而不是C,也即优先处理等待集中的线程而不是cxq竞争队列的线程。那么我们通过notify方法来看看实现原理。Notify便是Wait操作的反向操作,所以这里很简单,无非就是将线程从等待集中移出并且唤醒。源码如下。
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
// 直接调用ObjectSynchronizer::notify
ObjectSynchronizer::notify(obj, CHECK);
JVM_END
这里直接跟进ObjectSynchronizer::notify。源码如下。
void ObjectSynchronizer::notify(Handle obj, TRAPS) {
if (UseBiasedLocking) {
// 如果使用偏向锁,那么取消偏向锁
BiasedLocking::revoke_and_rebias(obj, false, THREAD);
}
markOop mark = obj->mark();
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
// 如果是轻量级锁,那么直接返回,因为wait操作需要通过对象监视器来做
return;
}
ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD);
}
可以看到最终调用了ObjectSynchronizer的notify方法来唤醒。源码如下。
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
// 如果等待集为空,直接返回
return ;
}
int Policy = Knob_MoveNotifyee ; // 移动策略,这里默认是2
Thread::SpinAcquire (&_WaitSetLock, “WaitSet - notify”) ; // 首先对等待集上自旋锁
// 调用DequeueWaiter将一个等待线程从等待集中拿出来
2021年Java中高级面试必备知识点总结
在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。
本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。
目录:
(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)
部分内容:
对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。
不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注
nv, jobject handle))
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
// 直接调用ObjectSynchronizer::notify
ObjectSynchronizer::notify(obj, CHECK);
JVM_END
这里直接跟进ObjectSynchronizer::notify。源码如下。
void ObjectSynchronizer::notify(Handle obj, TRAPS) {
if (UseBiasedLocking) {
// 如果使用偏向锁,那么取消偏向锁
BiasedLocking::revoke_and_rebias(obj, false, THREAD);
}
markOop mark = obj->mark();
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
// 如果是轻量级锁,那么直接返回,因为wait操作需要通过对象监视器来做
return;
}
ObjectSynchronizer::inflate(THREAD, obj())->notify(THREAD);
}
可以看到最终调用了ObjectSynchronizer的notify方法来唤醒。源码如下。
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
// 如果等待集为空,直接返回
return ;
}
int Policy = Knob_MoveNotifyee ; // 移动策略,这里默认是2
Thread::SpinAcquire (&_WaitSetLock, “WaitSet - notify”) ; // 首先对等待集上自旋锁
// 调用DequeueWaiter将一个等待线程从等待集中拿出来
2021年Java中高级面试必备知识点总结
在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。
本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。
目录:
[外链图片转存中…(img-BsLEPx9c-1715679974565)]
(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)
[外链图片转存中…(img-n0x0cOkL-1715679974565)]
部分内容:
[外链图片转存中…(img-iBZVSnKj-1715679974566)]
[外链图片转存中…(img-Oc23VbRr-1715679974566)]
[外链图片转存中…(img-o1OedFXx-1715679974566)]
对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。
不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注