多线程(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

相关文章

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Qt中实现多线程导出数据功能的四种方式小结

《Qt中实现多线程导出数据功能的四种方式小结》在以往的项目开发中,在很多地方用到了多线程,本文将记录下在Qt开发中用到的多线程技术实现方法,以导出指定范围的数字到txt文件为例,展示多线程不同的实现方... 目录前言导出文件的示例工具类QThreadQObject的moveToThread方法实现多线程QC

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

MySQL中读写分离方案对比分析与选型建议

《MySQL中读写分离方案对比分析与选型建议》MySQL读写分离是提升数据库可用性和性能的常见手段,本文将围绕现实生产环境中常见的几种读写分离模式进行系统对比,希望对大家有所帮助... 目录一、问题背景介绍二、多种解决方案对比2.1 原生mysql主从复制2.2 Proxy层中间件:ProxySQL2.3

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致