多线程与高并发(五)

news2024/11/17 20:19:34

【ReentrantLock源码】:

在这里插入图片描述
在这里插入图片描述

【AQS源码】:

在这里插入图片描述

【公平与非公平】:

【公平】:
线程想要获得一把锁,乖乖的去这把锁的等待队列里排队————公平。
【非公平】:
线程想要获得一把锁,不去排队,去插队,抢到就算我的。

【】:

final Thread current = Thread.currentThread();   //拿到当前线程。

【理解state】:

int c = getState();
》〉》
    protected final int getState() {
        return state;
    }
》〉》
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
private volatile int state;

}

AQS的核心就是这个state 以及监控state的一个双向链表(链表里每个节点装的都是线程)。
在这里插入图片描述
//这个state是volatile的 , 可以保证线程之间是可见的。根据子类的不同实现有不同的意义。
state下面跟了一个队列,这个队列是AQS内部所维护的一个队列。这个队列每一个里面都是一个Node , 这个Node是AQS的一个内部类:

static final class Node { ...
	volatile Thread thread;  //最重要的一个属性 , 所以这个队列是一个线程队列。

	volatile Node prev;  //前一个节点。

	volatile Node next;  //后一个节点。
... }

//双向链表。

【入队出队】:

并非是将整个队列用sync锁住 , 而是用CAS的方式 , 几个线程都往尾巴上加,谁先谁后呢?——用CAS的方式

【 nonfairTryAcquire 】:

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) { //用CAS的方式,期望值是0,尝试将其改成1.
                    setExclusiveOwnerThread(current);  //假如改成了,把当前线程改为独占state的线程,就说明我已经得到了这把锁,而且这把锁是互斥的。
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {  //如果说当前线程已经是独占的了。
                int nextc = c + acquires;   //往后加,加个1。
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

【画图方式】:

1)方法之间的调用图;
2)类之间的类图。

【day5课前复习】:

state代表什么——要看子类如何去实现。
AQS核心一个共享的数据(state),一堆互相抢夺的线程(在队列中)。

【AQS源码复习(1)】:

【 addWaiter 】:

Node.EXCLUSIVE————排它锁 ;
Node.SHARED————共享锁 ;

【AQS源码复习(2)】:

//用CAS去操作head和tail ;

【 VarHandle 】:

是1.9之后才有的;——指向某个变量的引用。本身已经有一个引用指向变量了,为什么再用一个VarHandle引用去指向变量呢?

	handle = MethodHandles.lookup().findVarHandle(T01_HelloVarHandle.class, "x", int.class);
	//到哪一个类中去找int类型的“x”变量。
	//这一句代码执行之后 , 通过x可以找到8 , 通过handle也可以找到8 。

【 意义所在 】:

handle.get(t)//直接读
handle.set(t,9);   //直接写
handle.compareAndSet(t, 9, 10);
handle.getAndAdd(t, 10);

//我们可以通过操作handle来改变所指向的值。
像x = x + 10 ; //这种操作如果想保证线程安全的话是需要加锁的 , 但是通过VarHandle就不需要(可以完成原子性的线程安全的操作)。

【反射和VarHandle的区别 】:

VarHandle效率要高的多,反射每次用之前都要做检查,VarHandle不需要(直接操作二进制码)。

【 总结VarHandle 】:

1):
普通属性原子操作;
2):
比反射快,直接操纵二进制码;

【 ThreadLocal 】:

static ThreadLocal<Person> tl = new ThreadLocal<>();   //Person这个值是线程独有的。

//设置的时候得设置自己线程能访问到的。

【解释双线程】:

线程一在ThreadLocal中设置了tl的值 , 但是线程二中却无法get , 这是为什么呢???

【 set源码 】:

    public void set(T value) {
        Thread t = Thread.currentThread();  //先拿到当前线程;
        ThreadLocalMap map = getMap(t);     //多了一个容器map
        if (map != null)
            map.set(this, value);  //this是当前的ThreadLocal对象。
        else
            createMap(t, value);
    }

map中的K是当前的ThreadLocal对象 , V是你要放的值。

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

==》〉
Thread类中:
ThreadLocal.ThreadLocalMap   threadLocals = null;

【总结SET的位置】:
在这里插入图片描述
//原来是设置到了当前线程的某一个Map之中去了。

【ThreadLocal的SET实质】:

设置到当前线程的Map中。

【 ThreadLocal用途 】:

声明式事务 , 保证同一个Connection 。

【 强引用 】:

//其实Java的引用一共有四种( 强、软、弱、虚 );
Obj o = new Obj() ; ————//此即强引用。只要有引用指向这个对象 , 垃圾回收器就不会去回收。

【 软引用 】:

SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024 * 10]);

//变量sr指向软引用对象 , 该对象又指向10M大小的字节数组。
在这里插入图片描述
//如果10M的数组被回收了 , 那么sr.get( ) 得到的应该是一个空值。

【 软引用实质 】:

当有一个对象被软引用所指向的时候 , 只有系统内存不够用的时候 , 才会回收这个对象。
//内存够用的话是不会回收的。

【内存分配】:

在这里插入图片描述

//堆内存直接分配20M (最多20M , 最小也是20M ,那么就是20M )

/**
 * 软引用
 * 软引用是用来描述一些还有用但并非必须的对象。
 * 对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。
 * 如果这次回收还没有足够的内存,才会抛出内存溢出异常。
 * -Xmx20M
 */
package Ten_Class.t05.no157;

import java.lang.ref.SoftReference;

public class T02_SoftReference {
    public static void main(String[] args) {
        SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024 * 10]);
        
        System.out.println(sr.get());
        System.gc();   //因为VM Options中设置了20M ,所以这里不会被回收。
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sr.get());

        //再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
        byte[] b = new byte[1024 * 1024 * 12];   //这里会被回收。
        System.out.println(sr.get());
    }
}
//软引用非常适合缓存使用

【最终输出】:
在这里插入图片描述

【 弱引用 】:

【概念】:
弱引用只要遭遇到gc就会回收。

【案例】:

/**
 * 弱引用遭到gc就会回收
 */
package Ten_Class.t05.no158;

import Ten_Class.t05.no156.M;
import Utils.SleepHelper;

import java.lang.ref.WeakReference;
import java.util.WeakHashMap;

public class T03_WeakReference {
    public static void main(String[] args) {
        WeakReference<M> wr = new WeakReference<>(new M());

        System.out.println(wr.get());
        System.gc();
        SleepHelper.sleepSeconds(1);
        System.out.println(wr.get());
    }
}

在这里插入图片描述

【作用】:

一般用在容器里。——一个典型的应用就是ThreadLocal

【 图解弱引用 】:

在这里插入图片描述
tl是强引用指向ThreadLocal对象 , 而key是弱引用指向ThreadLocal对象。

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

Entry的KEY是通过弱引用指向的ThreadLocal对象。

【ThreadLocal大坑】:

tl.remove();

//一定要养成好习惯——ThreadLocal用完了一定要进行remove , 否则会造成内存泄漏。

【 虚引用 】:

在这里插入图片描述

【作用】:

管理堆外内存。

【实质】:

是给写虚拟机的人用的。

【示例】:

/**
 * 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,
 * 也无法通过虚引用来获取一个对象的实例。
 * 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
 * 虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用活着弱引用关联着对象,
 * 那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,
 * 弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。
 * <p>
 * jdk中直接内存的回收就用到虚引用,由于jvm自动内存管理的范围是堆内存,
 * 而直接内存是在堆内存之外(其实是内存映射文件,自行去理解虚拟内存空间的相关概念),
 * 所以直接内存的分配和回收都是有Unsafe类去操作,java在申请一块直接内存之后,
 * 会在堆内存分配一个对象保存这个堆外内存的引用,
 * 这个对象被垃圾收集器管理,一旦这个对象被回收,
 * 相应的用户线程会收到通知并对直接内存进行清理工作。
 * <p>
 * 事实上,虚引用有一个很重要的用途就是用来做堆外内存的释放,
 * DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。
 */

package Ten_Class.t05.no159;

import Ten_Class.t05.no156.M;
import Utils.SleepHelper;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;

/**
*        //————务必先将堆内存设置成20M 。
* */

public class T04_PhantomReference {
    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);
        System.out.println(phantomReference.get());

        ByteBuffer b = ByteBuffer.allocateDirect(1024);

        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                SleepHelper.sleepSeconds(1);
                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();

        SleepHelper.sleepSeconds(1);
    }
}

在这里插入图片描述
//最后一直在永无休止地打印null …

【总结作用】:

当传入的一参对象被干掉的时候 ,你会收到一个通知( 通知的方式是往队列里扔进一个值 )。
//如果你想得到通知,你就不断去检测这个队列里面有没有值。 (如果有值说明某个虚引用被回收了)

【 虚引用 和 弱引用 的区别 】:

【弱】:
里面有值 , 你去get的时候,还是可以get到的;
【虚】:
你是get不到里面的值的。拿不着为什么这么设计呢——只是为了给你一个通知( 通知的时候放到队列里 )。
//虚引用一般是写JVM的人使用的 , 当监测到QUEUE中加入值的时候,可以做出相应的处理 。

【 DirectByteBuffer 】:

NIO里面的 , 叫做——直接内存。直接内存是不被JVM虚拟机直接管理的内存(被操作系统所管理),又叫做堆外内存。

【Q】:
当DirectByteBuffer被设置为NULL的时候 ,你怎么去回收堆外内存呢???——这个时候可以使用虚引用,当我们监测到这个虚引用被垃圾回收器回收的时候,做出相应的处理去回收堆外内存。

【 Unsafe 】:

[ 方法copyMemory ]:

//直接访问内存~

[ allocateMemory ]:

//直接分配内存。

[ freeMemory ]:

//手动回收——( 你直接分配内存的时候必须要手动回收 )。

【 总结 】:

在这里插入图片描述

【容器图】:

在这里插入图片描述
//容器图一定要背过!!!

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

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

相关文章

嵌入式实时操作系统的设计与开发(九)

同步机制 aCoral信号量机制不仅可以实现临界资源互斥访问&#xff0c;控制系统中临界资源多个实例的使用&#xff0c;还可以用于维护线程之间、线程和中断之间的同步。 当信号量用来实现同步时&#xff0c;起始值为0&#xff0c;如一个线程正在等待某个I/O操作&#xff0c;当…

【外贸小知识】通过whatsapp获取流量的几种小方法

相信做外贸的小伙伴们对于是whatsap比较熟悉的&#xff0c;都想通过whatsapp来获取更多流量&#xff0c;更多用户。今天我们花漾灵动小编就给大家汇总了通过whatsapp获取流量的几种小方法&#xff0c;希望能对新手小白有点作用哦&#xff01;通过whatsapp获取流量的几种小方法1…

【PyTorch深度学习实践】07_Dataset和Dataloader

文章目录1. Epoch&#xff0c;Iteration&#xff0c;Batch-Size2. Dataset 和 Dataloader2.1 Dataset2.2 Dataloader2.2.1 例子2.2.2 enumerate函数3. 完整代码1. Epoch&#xff0c;Iteration&#xff0c;Batch-Size 参考博客 2. Dataset 和 Dataloader 参考博客 功能概览 2…

2023年浙江建筑八大员(标准员)精选真题题库及答案

百分百题库提供建筑八大员&#xff08;标准员&#xff09;考试试题、建筑八大员&#xff08;标准员&#xff09;考试真题、建筑八大员&#xff08;标准员&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 14.根据《施工现场临…

Electron Vue之间的通讯 自定义标题栏实现最小化全屏关闭功能

方便以后定制化使用&#xff0c;学习记录一下。 话不多说&#xff0c;先看看效果吧。 效果 版本 electron ^13.0.0 知识点 Vue 相互通讯 Electron 标题栏主要逻辑代码 新建public\preload.js文件&#xff0c;用于前端全局发送和监听消息。 const { contextBridge, ipcRen…

【Linux】Linux权限的理解

文章目录&#x1f3aa; Linux权限的理解&#x1f680;1.shell命令及其运行原理&#x1f680;2.Linux权限概念⭐2.1 用户与root身份切换⭐2.2 用户与用户身份切换⭐2.3 单条指令提权&#x1f680;3.Linux文件权限⭐3.1 文件属性(第一个字符)⭐3.2 文件角色划分与文件属性⭐3.3 文…

线性代数第四章 向量组的线性相关性

向量组及其线性组合一.向量、向量组1.向量n个有次序的数a1,a2,...,an所组成的数组称为n维向量&#xff0c;这n个数称为该向量的n个分量&#xff0c;第i个数ai称为第i个分量n维向量可以写成一行&#xff0c;也可以写成一列&#xff0c;在没有指明是行向量还是列向量时&#xff0…

Authing 入选长城战略咨询《2022中国潜在独角兽企业研究报告》

12 月 23 日&#xff0c;长城战略咨询&#xff08;GEI&#xff09;发布《2022 中国潜在独角兽企业研究报告》&#xff08;下称《报告》&#xff09;。作为身份云行业领先的代表企业&#xff0c; Authing 凭借着过硬的技术实力和突出的创新能力&#xff0c;首次入选中国潜在独角…

软件测试工程师为什么要写测试用例?

软件测试工程师为什么要写测试用例&#xff1f;相信从事软件测试行业的从业者来讲&#xff0c;测试用例并不陌生。因为测试用例不仅仅是一组简单的文档&#xff0c;它包含前提条件、输入、执行条件和预期结果等等重要内容&#xff0c;并且能够完成一定的测试目的和需求。下面本…

深度学习(20)—— ConvNext 使用

深度学习&#xff08;20&#xff09;—— ConvNext 使用 本篇主要使用convnext做分类任务&#xff0c;其中使用convnext-tiny&#xff0c;其主要有5块 stage0stage1stage2stage3head 文章目录深度学习&#xff08;20&#xff09;—— ConvNext 使用Part 1 ModelPart 2 Traini…

【数据结构】一篇博客带你实现双向带头循环链表!!!(零基础小白也可以看懂)

目录 0.前言 1. 简述双向带头链表 2.双向带头循环链表的实现 2.1 设计双向带头循环链表结构体 2.2双向带头循环链表的初始化 2.3双向带头循环链表的尾插 2.4双向带头循环链表的尾删 2.5双向带头循环链表的头插 2.6双向带头循环链表的头删 2.7双向带头循环链表的插入 …

【面试题】notify() 和 notifyAll()方法的使用和区别

【面试题】notify() 和 notifyAll()方法的使用和区别 Java中notify和notifyAll的区别 何时在Java中使用notify和notifyAll&#xff1f; 【问】为什么wait()一定要放在循环中&#xff1f; Java中通知和notifyAll方法的示例 Java中通知和notify方法的示例 Java中notify和no…

22年我在CSDN做到了名利兼收

写在前面 hi朋友&#xff0c;我是几何心凉&#xff0c;感谢你能够点开这篇文章&#xff0c;看到这里我觉得我们是有缘分的&#xff0c;因着这份缘分&#xff0c;我希望你能够看完我的分享&#xff0c;因为下面的分享就是要汇报给你听的&#xff0c;这篇文章是在 2022 年 12 月 …

从0到1完成一个Vue后台管理项目(二十三、初代项目完成、已开源)

开源地址 项目地址 项目还在优化&#xff0c;会增加很多新功能&#xff0c;UI也会重新设计&#xff0c;已经在修改啦&#xff01; 最近打算加一些组件、顺便分享一些好用的开源项目 现在正在做迁移到vue3TS的版本、预计年后会完事&#xff0c;然后迁移到vite、遇到的问题和报…

docker安装prometheus和grafana

docker安装prometheus和grafana docker安装prometheus和grafana 概念简述安装prometheus 第一步&#xff1a;确保安装有docker第二步&#xff1a;拉取镜像第三步&#xff1a;准备相关挂载目录及文件第四步&#xff1a;启动容器第五步&#xff1a;访问测试 安装grafana 第一步&…

分享66个ASP源码,总有一款适合您

ASP源码 分享66个ASP源码&#xff0c;总有一款适合您 66个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1Jf78pfAPaFo6QhHWWHEq0A?pwdwvtg 提取码&#xff1a;wvtg 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&…

Docker容器与镜像命令

文章目录帮助命令镜像命令容器命令其它命令命令总结帮助命令 显示 Docker 版本信息 docker version显示 Docker 系统信息&#xff0c;包括镜像和容器数 docker info 帮助 docker --help 镜像命令 列出本地主机上的镜像 docker images运行结果 REPOSITORY TAG …

Python采集彼岸4K高清壁纸

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 解释器 Pycharm 编辑器 模块 import re import requests >>> pip install requests ( 更多资料、教程、文档点击此处跳转跳转文末名片加入君羊&#xff0c;找…

【Leetcode面试常见题目题解】5. 最长公共前缀

题目描述 本文是LC第14题&#xff0c;最长公共前缀&#xff0c;题目描述如下&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 限制 1 < strs.length < 200 0 < strs[i].length < 200 strs[i] 仅…

数据库 MySQL-window安装和卸载

安装 官网&#xff1a; MySQL :: Download MySQL Community Server 或 MySQL :: Download MySQL Community Server (Archived Versions) 文件目录简述 bin存放了可执行文件&#xff0c;docs是文档&#xff0c;include放的是c语言相关的.h文件&#xff0c;lib是c语言的库文件…