JVM并发编程AQSsync锁ReentrantLock线程池ThreadLocal

news2025/3/18 12:38:30

并发编程2

    • synchronized锁实现
    • **AQS**
    • **ReentrantLock实现**
    • **JUC 常用类**
    • 池的概念
  • ThreadLocal
    • ThreadLocal原理
    • 内存泄露
    • 强引用:
    • 软引用
    • 弱引用
    • 虚引用
    • ThreadLocal内存泄露

synchronized锁实现

synchronized是一个关键字,实现同步,还需要我们提供一个同步锁对象,记录锁状态,记录线程信息

控制同步,是依靠底层的指令实现的.

如果是同步方法,在指令中会为方法添加ACC_SYNCHRONIZED标志

如果是同步代码块,在进入到同步代码块时,会执行monitorenter, 离开同步代码块时或者出异常时,执行monitorexit

AQS

AQS(AbstractQueuedSynchronizer 抽象同步队列) 是一个实现线程同步的框架

并发包中很多类的底层都用到了AQS

class AbstractQueuedSynchronizer {
    private transient volatile Node head;

    private transient volatile Node tail;

    private volatile int state; //表示有没有线程访问共享数据  默认是0 表示没有线程访问
 
    //修改状态的方法
     protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
    
     static final class Node {
         volatile Node prev;
         volatile Node next;
         volatile Thread thread;
     }
    
}

ReentrantLock实现

ReentrantLock完全同过java代码控制

 class ReentrantLock{
     abstract static class Sync extends AbstractQueuedSynchronizer {
            abstract void lock();
     }
     //非公平锁
     static final class NonfairSync extends Sync {
          void lock(){
              
          }
         
     }
     //公平锁
     static final class FairSync extends Sync {
         void lock(){
              
          }
     }
 }

在这里插入图片描述

JUC 常用类

在集合类中,像Vector,Hashtable这些类加锁时都是直接把锁加载方法上了,性能就低, 在并发访问量小的情况下,还可以使用, 大并发访问量下,性能就太低了.

ConcurrentHashMap

 HashMap适合单线程场景下的,不允许多个线程同时访问操作,如果有多线程访问会报异常
 Hashtable 是线程安全的 直接给方法加锁,效率低
 ConcurrentHashMap 是线程安全的,没有直接给方法加锁, 用哈希表中每一个位置上的第一个元素(第一个是存在元素)作为锁对象
      哈希表长度是16,那么就有16把锁对象,锁住自己的位置即可,
      这样如果多个线程如果操作不同的位置,那么相互不影响,只有多个线程操作同一个位置时,才会等待
      如果位置上没有任何元素,那么采用cas机制插入数据到对应的位置
         Hashtable ,ConcurrentHashMap  键值都不能为null
         为什么这样设计,键值都不能为null?
          map.put("b","b")
         为了消除歧义  System.out.println(map1.get("a"));//null  值是null  还是键不存在返回null

CopyOnWriteArrayList

ArrayList 是单线程场景下使用的,在多线程场景下会报异常
Vector 是线程安全的,在方法上加了锁,效率低
CopyOnWriteArrayList  写方法操作加了锁(ReentrantLock实现的),
在写入数据时,先把原数组做了备份,把要添加的数据写入到备份数组中,当写入完成后,再把修改的数组赋值到原数组中去
给写加了锁,读没有加锁,读的效率变高了, 这种适合写操作少,读操作多的场景

CopyOnWriteArraySet

CopyOnWriteArraySet 的实现基于 CopyOnWriteArrayList,不能存储重复数据。

辅助类 CountDownLatch

池的概念

字符串常量池

String s1 = “abc”; String s2=“abc”; s1==s2//true

Integer自动装箱 缓存了-128 --+127之间的对象

Integer a = 100; Integer b = 100; a==b //true IntegerCache.cache[i + (-IntegerCache.low)];

数据库连接池

阿里巴巴Druid数据库连接池

帮我们缓存一定数量的链接对象,放在池子里,用完还回到池子中,

减少了对象的频繁创建和销毁的时间开销

线程池

在这里插入图片描述

为减少频繁的创建和销毁线程,

jdk5开始引入了线程池,

建议使用ThreadPoolExecutor类来创建线程池, 这样提高效率.

Java.uitl.concurrent.ThreadPoolExecutor

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

7个参数

corePoolSize: 核心线程池中的数量(初始化的数量) 5

maximumPoolSize:线程池中最大的数量 10 5

keepAliveTime: 空闲线程存活时间 当核心线程池中的线程足以应付任务时, 非核心线程池中的线程在指定空闲时间到期后,会销毁.

unit: 时间单位

workQueue: 5 等待队列, 当核心线程池中的线程都在使用时,如果有任务继续到来,会先将等待的任务放到队列中,如果队列也满了,才会创建新的线程(非核心线程池中的线程)

threadFactory:线程工厂,用来创建线程池中的线程

handler:拒绝处理任务时的策略 4种拒绝策略

线程池工作流程

当有大量的任务到来时,先判断核心线程池中的线程是否都忙着,

​ 有空闲的,直接让核心线程中的线程执行任务

​ 没有空闲的, 判断等待队列是否已满,

​ 如果没满,把任务添加到队列等待

​ 如果已满,判断非核心线程池中的线程是否都忙着

​ 如果有空闲的,没满,交由非核心线程池中的线程执行

​ 如果非核心线程池野已经满了,那么就使用对应的拒绝策略处理.

4种拒绝策略:

AbortPolicy: 抛异常

CallerRunsPolicy: 由提交任务的线程执行 例如在main线程提交,则由main线程执行拒绝的任务 DiscardOldestPolicy: 丢弃等待时间最长的任务

DiscardPolicy: 丢弃最后不能执行的任务

提交任务的方法

void   execute(任务);  提交任务没有返回值

Future<?> submit = executor.submit(myTask);//提交任务可以接收返回值
submit.get();  

关闭线程池

shutdown();  执行shutdown()后,不再接收新的任务,会把线程池中还有等待队列中已有的任务执行完,再停止
shutdownNow(); 立即停止,队列中等待的任务就不再执行了.
  如果有空闲的,没满,交由非核心线程池中的线程执行

​ 如果非核心线程池野已经满了,那么就使用对应的拒绝策略处理.

4种拒绝策略:

AbortPolicy: 抛异常

CallerRunsPolicy: 由提交任务的线程执行 例如在main线程提交,则由main线程执行拒绝的任务 DiscardOldestPolicy: 丢弃等待时间最长的任务

DiscardPolicy: 丢弃最后不能执行的任务

提交任务的方法

void   execute(任务);  提交任务没有返回值

Future<?> submit = executor.submit(myTask);//提交任务可以接收返回值
submit.get();  

关闭线程池

shutdown();  执行shutdown()后,不再接收新的任务,会把线程池中还有等待队列中已有的任务执行完,再停止
shutdownNow(); 立即停止,队列中等待的任务就不再执行了.

ThreadLocal

ThreadLocal中填充的变量属于当前线程,改变量对其他线程而言是隔离的
ThreadLocak为变量在每个线程创建了一个副本,每个线程可以访问自己内部的副本变量

static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
	protected Integer initialValue() {
	return 1;
	}
};

ThreadLocal原理

首 先 ThreadLocal 是 一 个 泛 型 类 , 保 证 可 以 接 受 任 何 类 型 的 对 象 ,ThreadLocal 内 部 维 护 了 一 个 Map , ThreadLocal 实 现 了 一 个 叫做 ThreadLocalMap 的静态内部类。
而我们使用的 get()、set() 方法其实都是由这个 ThreadLocalMap 类对应的 get()、set() 方法实现的。首 先 ThreadLocal 是 一 个 泛 型 类 , 保 证 可 以 接 受 任 何 类 型 的 对 象 。

在这里插入图片描述
在这里插入图片描述
最终变量是放在当前线程的ThreadLocalMap中,并不是存在ThreadLocal上,ThreadLocal主要作为key,用于存读操作

内存泄露

当对象已经不再被引用,但是垃圾回收机制无法回收该对象,就会产生内存泄露问题(例如数据库链接对象,流对象,socker)

强引用:

当内存不足,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,死都不收。

强引用是我们最常见的普遍对象引用,只要还有强引用指向一个对象,就能表明对象还活着,垃圾收集器不会碰这种对象。在JAVA最常见的就是强引用,把一个对象赋给一个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到JVM也不会回收,因此强引用时造成Java内存泄漏的主要原因之一。

对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用复制为null,一版认为就是可以背垃圾收集了。

public class StrongReferenceDemo {
    public static void main(String []args) {
        Object obj1 = new Object(); //这样定义默认是强引用
        Object obj2 = obj1;
        obj1 = null;
        System.gc();
        System.out.println(obj1);
        System.out.println(obj2);
    }
}

对象如果有强引用关系,必定不会被回收

软引用

软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾收集,对于只有软引用的对象来说,

当系统内存充足时它不会被回收,当系统内存不足时它会被回收。

软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收!

public class SoftReferenceDemo {
    public static void main(String []args) {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<Object>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        o1 = null;
        System.gc();
        try {
            byte[] bytes = new byte[30*1024*1024];
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }

    }
}

弱引用

弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存区更短。

对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。

public class WeakReferenceDemo {
    public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference<Object> weakReference = new WeakReference<Object>(o1);
        System.out.println(o1);
        System.out.println(weakReference.get());

        o1 = null;
        System.gc();
        System.out.println("----------------------------");
        System.out.println(o1);
        System.out.println(weakReference.get());
    }
}

虚引用

虚引用需要java.lang.ref.PhantomReference类来实现。

顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时间都可能被垃圾回收

器回收,它不能单点使用也不能通过它访问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。

虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象,其意义在于说明一个对象已经进入了finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。

换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用fianlize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作

public class PhantomReferenceDemo {

    public static void main(String []args) throws Exception {
        Object o1 = new Object();
        ReferenceQueue<Override> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference(o1,referenceQueue);
        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println("-----------------");
        o1 = null;
        System.gc();
        Thread.sleep(500);

        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());

    }
}

ThreadLocal内存泄露

在这里插入图片描述
TreadLocalMap 使用 ThreadLocal 的弱引用作为 key,如果一个 ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被 GC 回收,这样就会导致ThreadLocalMap 中 key 为 null, 而 value 还存在着强引用,只有 thead 线程退出以后,value 的强引用链条才会断掉。
但如果当前线程再迟迟不结束的话,这些 key 为 null 的 Entry 的 value 就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value

永远无法回收,造成内存泄漏。
ThreadLocal 正确的使用方法
每次使用完 ThreadLocal 都调用它的 remove()方法清除数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是数学建模?数学建模是将实际问题转化为数学问题

数学建模是将实际问题转化为数学问题&#xff0c;并通过数学工具进行分析、求解和验证的过程。 一、数学建模的基本流程 问题分析 • 明确目标&#xff1a;确定需要解决的核心问题。 • 简化现实&#xff1a;识别关键变量、忽略次要因素。 • 定义输入和输出&#xff1a;明确模…

唤起“队列”的回忆

又来博客记录自己的学习心得了&#xff0c;嘿嘿嘿(^&#xff5e;^) 目录 队列的概念和结构&#xff1a; 队列的创建和初始化&#xff1a; 队列入栈&#xff1a; 队列出栈&#xff1a; 队列的销毁&#xff1a; 取队头和队尾数据&#xff1a; 结语&#xff1a; 队列的概念…

Linux(8.4)NFS

文章目录 一、概念二、详解NFS1&#xff09;软件名2&#xff09;服务名3&#xff09;配置文件4&#xff09;端口号5&#xff09;相关命令 三、部署NFS一、NFS服务端1&#xff09;**配置源&#xff08;本地或者网络源&#xff09;**2&#xff09;2、安装NFS**3&#xff09;启动服…

【位运算】速算密钥:位运算探秘

文章目录 前言例题一、判定字符是否唯一二、丢失的数字三、两整数之和四、只出现⼀次的数字 II五、消失的两个数字 结语 前言 什么是位运算算法呢&#xff1f; 位运算算法是以位运算为核心操作&#xff0c;设计用来高效解决特定问题的一系列计算步骤集合。它巧妙利用位运算直接…

STM32G070CBT6读写FLASH中的数据

向FLASH中写入数据函数 /*函数说明&#xff1a;向FLASH中写数据形参&#xff1a;addr-要写入数据的起始地址 data-准备写入数据 len-数据大小返回值&#xff1a;1-成功&#xff0c;0-失败 */ uint8_t FlashWriteData(uint64_t addr,uint8_t data[],size_t len) {uint32_t Fir…

AD绘图基本操作

一、基本操作 注意&#xff1a;快捷键都要在英文模式下才能生效 1、移动 按住鼠标右键移动 2、切换桌面栅格距离 G 3、英寸和毫米 尺寸切换 Q 4、元件在3D模式下的移动 3D视角鼠标左键只起到选择元器件并移动之的功能&#xff0c; 单纯鼠标右键只能平移桌面 shift鼠…

dfs(十二)21. 合并两个有序链表 递归解决

21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] …

51单片机指令系统入门

目录 基本概念讲解 一、机器指令​ 二、汇编指令​ &#xff08;一&#xff09;汇编指令的一般格式 &#xff08;二&#xff09;按字节数分类的指令 三、高级指令 总结​ 基本概念讲解 指令是计算机&#xff08;或单片机&#xff09;中 CPU 能够识别并执行的基本操作命令…

安全无事故连续天数计算,python 时间工具的高效利用

安全天数计算&#xff0c;数据系统时间直取&#xff0c;安全标准高效便捷好用。 笔记模板由python脚本于2025-03-17 23:50:52创建&#xff0c;本篇笔记适合对python时间工具有研究欲的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&am…

如何玩DeepSeek!15分钟快速创建GIS动态数据可视化仪表盘

DeepSeek最近火遍全球&#xff0c;大家用的都用的不亦乐乎。国外呢&#xff1f;当然也是&#xff0c;最近一上YouTube、X等都是deepseek的推送。 今天介绍一下&#xff0c;我在YouTube上看到的GIS行业与DeepSeek结合的一个案例&#xff1a; 快速轻松构建交互式地图仪表盘&…

课上测试:MIRACL共享库使用测试

MIRACL(MultiprecisionIntegerandRationalArithmeticC/cLibrary)是著名的密码算法库&#xff0c;设法去官网下载安装MIRACL&#xff0c;提交安装过程截图或过程文本&#xff08;3分&#xff09;. 去github官网下载.zip文件 使用如下命令进行解压 unzip -j -aa -L MIRACL-mast…

网络编程知识预备阶段

1. OSI七层模型 OSI&#xff08;Open System Interconnect&#xff09;七层模型是一种将计算机网络通信协议划分为七个不同层次的标准化框架。每一层都负责不同的功能&#xff0c;从物理连接到应用程序的处理。这种模型有助于不同的系统之间进行通信时&#xff0c;更好地理解和…

STM32微控制器_03_GPIO原理与应用

核心内容 STM32 GPIO基本原理&#xff08;熟悉&#xff09;GPIO输出功能HAL库编程实现的应用&#xff08;重点&#xff09;GPIO输入功能HAL库编程实现的应用&#xff08;重点&#xff09; 一.STM32 GPIO基本原理 1.GPIO简介 STM32的GPIO相当于STM32的四肢&#xff0c;一个S…

零拷贝分析

kafka 零拷贝 请求 - 网口 - socket - 用户态 - 内核缓存区 - 内核态&#xff08;磁盘信息&#xff09; 磁盘 - 内核缓存区 - 用户缓存区 - 网络缓存区 零拷贝&#xff08;Zero-Copy&#xff09; 是一种高效的数据传输技术&#xff0c;旨在减少数据在内存中的拷贝次数&#x…

从Instagram到画廊:社交平台如何改变艺术家的展示方式

从Instagram到画廊&#xff1a;社交平台如何改变艺术家的展示方式 在数字时代&#xff0c;艺术家的展示方式正在经历一场革命。社交平台&#xff0c;尤其是Instagram&#xff0c;已经成为艺术家展示作品、与观众互动和建立品牌的重要渠道。本文将探讨社交平台如何改变艺术家的…

✎ 一次有趣的经历

&#x1f4c6;2025年3月17日 | 周一 | ☀️晴 &#x1f4cd;今天路过学院楼7&#xff0c;见到了满园盛开的花&#x1f33a;&#xff0c;心情瞬间明朗&#xff01; &#x1f4cc;希望接下来的日子也能像这些花一样&#xff0c;充满活力&#x1f525;&#xff01; &#x1…

快!快!快!NDPP时延测试数据公布!

在全方位认识NDPP第3期《NDPP在金融场景的应用》中&#xff0c;我们重点介绍了NDPP的典型应用场景行情解码硬件加速和策略计算加速&#xff0c;并帮助某百亿私募用户基于NDPP实现期货业务加速的案例。 近期&#xff0c;中科驭数凭借低时延产品荣获信创“大比武”行业融合赛道三…

激光雷达“开卷”2.0,头部Tier1入局

高阶智驾的普及&#xff0c;正在催生激光雷达市场的巨大潜在增长空间。 本周&#xff0c;汽车激光雷达主力供应商之一的禾赛科技发布财报&#xff0c;去年第四季度激光雷达总交付量为222,054台&#xff0c;同比增长153.1%&#xff0c;超过2023年全年。2024全年激光雷达总交付量…

力扣No.376.摆动序列

题目&#xff1a; 链接&#xff1a; https://leetcode.cn/problems/wiggle-subsequence/description/ 代码&#xff1a; class Solution {public int wiggleMaxLength(int[] nums) {int nnums.length;//状态表示:int[] fnew int[n];int[] gnew int[n];//初始化:for(int i0;i…

C语言中qsort函数的详解,以及模拟

引言 C语言中qsort函数的详解和模拟实现qsort函数&#xff0c;这里为了使用冒泡排序来模拟qsort函数 一、详解qsort函数 在 C 语言中&#xff0c;qsort 函数是一个标准库函数&#xff0c;用于对数组进行快速排序&#xff08;Quick Sort&#xff09;。它位于 <stdlib.h>…