AQS源码解析
AQS:AbstractQueuedSynchronizer
以CAS为核心,替代了传统的synchronized
了解一下VarHandle:
可以简单理解为类似于指针,指向一个对象,可以设置属性,普通属性也可以进行原子操作
JDK9之前没有,直接操作二进制码,比反射快多了,
以ReentrantLock.lock()为例
实际调用的方法如下:
ReentrantLock.class
如果CAS操作能够成功执行,就把当前线程设置成独占锁的线程
如果CAS操作不能成功执行,就去获得锁
AQS类:
acquire():
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
剩下内容自己去打断点查看以及翻阅上一节课内容
ThreadLocal
设置线程独有的内容,其他线程访问不到
package character05;
import java.util.concurrent.TimeUnit;
/**
* @author laimouren
*/
public class ThreadLocal02 {
static class Person{
String name = "zhangsan";
}
static ThreadLocal<Person> tl = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(tl.get());
}).start();
new Thread(()->{
try{
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
tl.set(new Person());
}).start();
}
}
输出结果为空,因为线程1只能访问到他自己的tl,而他自己的tl没有设置Person对象
源码
set方法:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
其中的map对象是谁呢?
查看getMap方法
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
按住ctrl点击threadLocals,会发现threadLocals是Thread类的一个属性
也就是说那个map是当前线程的一个属性,所以也就知道了为什么其他线程访问不到当前线程修改的内容
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal用途
- 声明式事务,保证同一个Connection
引用类型
java有四种引用:强软弱虚
引用:一个变量指向一个对象,这个变量就是一个引用
强引用
package character05.RefType;
public class M {
/**
* 垃圾回收时会自动调用该方法
* 这里只是为了观察,日常使用不应该重写他
* @throws Throwable
*/
@Override
protected void finalize()throws Throwable{
System.out.println("finalize");
}
}
package character05.RefType;
import java.io.IOException;
/**
* 普通引用,也就是强引用
* 只有有一个引用指向该对象,该对象一定不会被回收
* @author laimouren
*/
public class NormalReference {
public static void main(String[] args) throws IOException {
M m = new M();
//如果注释掉下面这句话,则不会回收M对象
m = null;
System.gc();
//为了阻塞当前线程,因为垃圾回收是在其他线程
System.in.read();
}
}
软引用
当一个对象被一个软引用指向时,只有系统内存不够用了,才会回收它
主要用于缓存
下面测试例子执行前需添加程序jvm设置:-Xms20M -Xmx20M
package character05.RefType;
import java.lang.ref.SoftReference;
/**
* 软引用
* @author laimouren
*/
public class SoftReference01 {
public static void main(String[] args) {
SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
//m = null;
System.out.println(m.get());
System.gc();
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(m.get());
//在分配一个数组,heap将装不下,这时候系统就会垃圾回收,先回收一次,如果不够,将软引用干掉
byte[] b = new byte[1024*1024*10];
System.out.println(m.get());
}
}
弱引用(WeakReference)
作业:了解一下WeakHashMap的作用
作用:如果同时有一个强引用和一个弱引用指向同一个对象,当强引用消失时,该对象就应该被回收,一般用在容器里
应用:ThreadLocal
package character05.RefType;
import java.lang.ref.WeakReference;
/**
* 弱引用只要遭到gc就会被回收
* @author laimouren
*/
public class WeakReference01 {
public static void main(String[] args) {
WeakReference<M> m = new WeakReference<>(new M());
System.out.println(m.get());
System.gc();
System.out.println(m.get());
System.out.println("————————");
ThreadLocal<M> tl = new ThreadLocal<>();
tl.set(new M());
//记得remove,不然会导致内存泄漏
tl.remove();
}
}
虚引用
不是给程序员用的,给写虚拟机的人用的
甚至使用get方法也得不到对象
作用:管理堆外内存:因为JVM不能直接回收堆外内存,所以我们用一个队列监控堆外内存的虚引用,当虚引用被回收,则队列会有值,此时我们就去回收堆外内存
先设置JVM最大内存最小内存-Xms20M -Xmx20M
package character05.RefType;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;
/**
* 虚引用
* @Author laimouren
* @Date 2022/1/4 10:34
*/
public class PhantomReference01 {
private static final List<Object> LIST = new LinkedList<>();
private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
public static void main(String[] args) {
PhantomReference<M> phantomReference = new PhantomReference<>(new M(),QUEUE);
new Thread(()->{
while (true) {
LIST.add(new byte[1024 * 1024]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//虚引用get不到里面的值,但是实际是存在的
System.out.println(phantomReference.get());
}}).start();
new Thread(()->{
while (true){
Reference<? extends M> poll = QUEUE.poll();
if (poll != null){
System.out.println("-----虚引用对象被jvm回收了-----"+poll);
}
}
}).start();
try{
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}