多线程(3)-源码分析(ReentrantLock-AQS,ThreaLocal)、强软弱虚引用

本文主要是介绍多线程(3)-源码分析(ReentrantLock-AQS,ThreaLocal)、强软弱虚引用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. synchronized

public  void hello(){synchronized (this){System.out.println("a");}
}

经过编译的字节码

 0 aload_01 dup2 astore_13 monitorenter4 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>7 ldc #3 <a>9 invokevirtual #4 <java/io/PrintStream.println : (Ljava/lang/String;)V>
12 aload_1
13 monitorexit
14 goto 22 (+8)
17 astore_2
18 aload_1
19 monitorexit
20 aload_2
21 athrow
22 return

monitorenter:进入对象监视器
monitorexit:退出监视器,释放锁。两个monitorexit的原因是1. 当正常执行完结束时,monitorexit退出。2. 当执行报错时,自动monitorexit,自动退出。

synchronized实现的原理:
每个对象都与一个监视器相关联。当且仅当监视器有所有者时,监视器才被锁定。执行monitorenter的线程 尝试获得与对象关联的监视器的所有权,如下所示:
如果与对象关联的监视器的条目计数 为零,则线程进入监视器并将其条目计数设置为 1。该线程然后是监视器的所有者。
如果线程已经拥有与对象关联的监视器 ,它会重新进入监视器,增加其条目计数。
如果另一个线程已经拥有与对象关联的监视器 ,线程会阻塞,直到监视器的条目计数为零,然后再次尝试获得所有权。

  1. 多线程同时调用synchronized块时,其中一个线程进入对象监视器,将条目计数为1,该线程然后是监视器的所有者,持有对象锁。
  2. 其他的线程进入监视器失败,会进入阻塞队列中
  3. 当持有锁的对象释放锁,监视器条目计数-1,当为0时,通知队阻塞队列中的线程尝试进入监视器,如果进入成功,该线程就持有锁。
  4. 如果持有锁的线程又一次去进入监视器,那么条目计数加1. synchronized重入。
    在这里插入图片描述

2. ReenTrantLock-AQS

  • volatile int state
  • cas
  • 原理
    https://blog.csdn.net/fuyuwei2015/article/details/83719444
  • lock() 源码
  • unlock() 源码

3. ThreadLocal

  • ThreadLocal 称为ThreadLocal变量,线程局部变量。一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自己线程的值)。也就是说,同一个ThreadLocal对象,在不同的线程中是不一样的。
  • API
    set方法,向ThreadLocal中设置当前线程的的值。
    get方法,获取当前线程在ThreadLocal中设置的值。
  • 为什么叫局部变量,可以把这个对象当成一个局部变量。变量person 等同于 threadLocal。使用时直接threadLocal.get();
    每一个threadLocal中都可以保存一个值。
Person person = new Person();
//相当于
ThreadLocal<Person> threadLocal = new ThreadLocal<Person>();
threadLocal.set(new Person());
  • 上代码
package com.tzw.juc.threadLocal;public class C11_threadLocal {private static ThreadLocal<Person> threadLocal = new ThreadLocal<Person>();public static void main(String[] args) {//主线程// 1. 在同一线程中,threadLocal相当于一个局部变量,可以进行赋值操作。// 2. 同一个线程中,threadLocal可以修改这个局部变量的值。C11_threadLocal c11_threadLocal = new C11_threadLocal();threadLocal.set(c11_threadLocal.new Person("张三"));threadLocal.set(c11_threadLocal.new Person("王武"));Person person = threadLocal.get();System.out.println(person);//线程1//1. 输出结果为null,说明ThreadLocal对象虽然是同一个,但是不同的线程中threadLocal对象保存的值不一样,//线程不共享。new Thread(()->{Person person1 = threadLocal.get();System.out.println(person1);}).start();//线程2// 输出结果为:李四。说明同一个threadLocal对象,不同线程中ThreadLocal保存的值是不一样的。new Thread(()->{threadLocal.set(c11_threadLocal.new Person("李四"));Person person2 = threadLocal.get();System.out.println(person2);}).start();}public class Person{String name;public Person(String name){this.name=name;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}}
}

输出结果:

Person{name=‘王武’}
null
Person{name=‘李四’}

  • 原理:源码

看一个set方法的源码

public void set(T value) {//1. 获取当前线程Thread t = Thread.currentThread();//2. 获取当前线程对象中的mapThreadLocal.ThreadLocalMap map = getMap(t);//map不为空,设置值if (map != null)map.set(this, value);//map为空,创建一个mapelsecreateMap(t, value);}//从当前线程对象中获取threadLocals属性。ThreadLocal.ThreadLocalMap getMap(Thread t) {return t.threadLocals;}void createMap(Thread t, T firstValue) {//给t线程对象的threadLocals属性赋值一个ThreadLocalMapt.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);}

总结:

  1. set值时实际上是set到map中。
  2. 而这个map是维护在Thread类中的。所以也就是说每个一个Thread对象都会维护一个自己的Map。通过ThreadLocal设的值(set)其实是存放在当前线程的map属性中的。这也就解释了同一个threadLocal对象设置的值在不同的线程中是不共享的。每个线程都会维护一个map来存放。
  3. ThreadLocal的应用场景:声明式事物。

一个事物方法,方法中有对数据库增删改等操作,操作需要获取connect连接,这一个事物中的连接必须都一样,所以在ThreadLocal中设置一个connect,每一个线程调用这个方法的时候都有自己维护的connect。

4.强软弱虚引用

4.1 强引用

一般情况下普通的引用都是强引用。
eg:

/*** 强引用* 在强引用消失,在垃圾回收的时候会被回收。*/
public class NormalReference {public static void main(String[] args) {A a = new A();a=null;System.gc();while(true){}}
}

类似于这种的都是强引用。
当强引用消失的时候才能够通过System.gc()进行回收垃圾,将person对象回收。

重写Object的finalize()方法。当该对象被回收时会调用finalize方法。我们可以通过观察finalize方法的调用判断A对象是否被回收。


/*** 重写Object类的finalize方法* 该方法在进行回收本类对象的时候会调用。* 回收A对象时会被调用*/
public class A {@Overridepublic void finalize(){System.out.println("进行垃圾回收");}
}

4.2 软引用

  1. 软引用在内存足够的时候发生gc不会被回收,只有在jvm内存不够使用的时候进行回收。
  2. 软引用适用于做缓存。
package com.tzw.juc.reference;import java.lang.ref.SoftReference;import static java.lang.Thread.sleep;/**** 1. softReference---》new SoftReference() 强引用*    SoftReference()  -----> new byte[1024*1024*10] 软引用* 2. 设置软引用分配 10M内存* 3. 调用System.gc(),进行垃圾回收* 4. 回收后重新看软引用是否被回收* 5. 在分配15M内存空间* 6. 查看软引用是否被回收** 设置vm参数,-Xms20M -Xmx20M* 执行结果:** [B@28d93b30* [B@28d93b30* null** 总结:软引用内存够大时,gc不会回收软引用,* 当内存不足时,gc会回收软引用。* 当强引用消失时, new SoftReference()和 软引用都会被回收。**/
public class C_SoftReference {public static void main(String[] args) {SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024*1024*10]);System.out.println(softReference.get());System.gc();try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(softReference.get());byte[] b = new byte[1024*1024*15];System.out.println(softReference.get());}
}

4.3 弱引用

  1. 只要发生gc就会回收弱引用,适用于ThreadLocal
package com.tzw.juc.reference;import java.lang.ref.WeakReference;public class C_WeakReference {public static void main(String[] args) {WeakReference<A> weakReference = new WeakReference<>(new A());System.out.println(weakReference.get());System.gc();//执行为null,弱引用在gc时一定会被回收。System.out.println(weakReference.get());//使用弱引用场景,ThreadLocalThreadLocal threadLocal = new ThreadLocal();threadLocal.set(new A());threadLocal.remove();// 可以当作// map中为 map(WeakReference(ThreadLoack),v);// threadLocal --->new ThreadLocal()强引用,threadLocal强引用指向ThreadLocal对象// map 中的key---》new WeakReference()---->ThreadLocal(弱引用),map中key弱引用指向ThreadLocal对象//分析:如果是强引用的话,如果threadLocal=null时,// 当gc的时候,由于key强引用指向ThreadLocal对象那么,ThreadLocal就永远不会被回收。造成内存泄露。//但是弱引用时,虽然gc会回收弱引用,但是相当于key=null,value值访问不到,那么也会造成内存泄露。//所以使用ThreadLocal时,如果使用完后,需要将remove方法。}}

4.4 虚引用

  1. 只要发生gc就会回收弱引用。
  2. 适用于堆外内存回收。由于堆外引用不在jvm内存中,所以无法直接控制,只能同过虚引用控制,当发现队列中有对象时,就去回收堆外内存。
package com.tzw.juc.reference;import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;/*** 虚引用时指向直接内存(堆外内存的)不在jvm堆中* 1 虚引用对象不能同个get获取到虚引用的对象* 2 虚引用对象在gc时一定会被回收* 3 虚引用对象被回收之后会放入到引用队列中。就是说引用队列中的对象已经是被回收过的。*/
public class C_PhantomReference {//创建一个引用队列private static final ReferenceQueue<A> QUEUE = new ReferenceQueue<>();public static void main(String[] args) {PhantomReference<A> phantomReference = new PhantomReference(new A(), QUEUE);// 获取A对象System.out.println(phantomReference.get());new Thread(() -> {while (true) {Reference<? extends A> poll = QUEUE.poll();if (poll != null) {System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);}}}).start();List<byte[]> list = new ArrayList<>();while(true){list.add(new byte[1024*1024]);}}
}

这篇关于多线程(3)-源码分析(ReentrantLock-AQS,ThreaLocal)、强软弱虚引用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/953901

相关文章

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性

Linux中的more 和 less区别对比分析

《Linux中的more和less区别对比分析》在Linux/Unix系统中,more和less都是用于分页查看文本文件的命令,但less是more的增强版,功能更强大,:本文主要介绍Linu... 目录1. 基础功能对比2. 常用操作对比less 的操作3. 实际使用示例4. 为什么推荐 less?5.

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

python多线程并发测试过程

《python多线程并发测试过程》:本文主要介绍python多线程并发测试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、并发与并行?二、同步与异步的概念?三、线程与进程的区别?需求1:多线程执行不同任务需求2:多线程执行相同任务总结一、并发与并行?1、

Java集成Onlyoffice的示例代码及场景分析

《Java集成Onlyoffice的示例代码及场景分析》:本文主要介绍Java集成Onlyoffice的示例代码及场景分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 需求场景:实现文档的在线编辑,团队协作总结:两个接口 + 前端页面 + 配置项接口1:一个接口,将o