java JUC并发编程 第七章 原子操作类增强

news2025/1/21 16:38:37

系列文章目录

第一章 java JUC并发编程 Future: link
第二章 java JUC并发编程 多线程锁: link
第三章 java JUC并发编程 中断机制: link
第四章 java JUC并发编程 java内存模型JMM: link
第五章 java JUC并发编程 volatile与JMM: link
第六章 java JUC并发编程 CAS: link
第七章 java JUC并发编程 原子操作类增强: link


文章目录

  • 系列文章目录
  • 1 概述
  • 2 分类说明
    • 2.1 基本类型原子类
      • 2.1.1 常用API简介
      • 2.1.2 case(不推荐)
      • 2.1.3 CountDownLatch优化上面case
    • 2.2 数组类型原子类
      • 2.2.1 case
    • 2.3 引用类型原子类
      • 2.3.1 AtomicReference,(前面的自旋锁Demo)
      • 2.3.2 AtomicStampedReference(前面的ABADemo)
      • 2.3.3 AtomicMarkableReference
    • 2.4 对象的属性修改原子类
      • 2.4.1 AtomicIntegerFieldUpdaterDemo(针对字段是int类型的)
      • 2.4.2 AtomicReferenceFieldUpdater(其他类型的字段)
    • 2.5 原子操作增强类原理深度解析
      • 2.5.1 点赞计数器
      • 2.5.2 性能评估
      • 2.5.3 原理、源码分析
        • 2.5.3.1 Striped64
        • 2.5.3.2 Striped64中一些变量或者方法的定义
        • 2.5.3.3 Cell单元格类
        • 2.5.3.4 LongAdder为什么这么快
        • 2.5.3.5 源码分析
          • 2.5.3.5.1 longAdder.increment()
          • 2.5.3.5.2 longAccumulate
          • 2.5.3.5.3 线程hash值:probe
          • 2.5.3.5.4 总纲
          • 2.5.3.5.5 计算
  • 3 总结


1 概述

java.util.concurrent.atomic 下的类
https://www.runoob.com/manual/jdk11api/java.base/java/util/concurrent/atomic/package-summary.html
在这里插入图片描述

2 分类说明

2.1 基本类型原子类

AtomicInteger,AtomicBoolean,AtomicLong

2.1.1 常用API简介

public final int get()//获取当前的值
public final int getAndSet(int newValue)//获取当前的值,并设置新的值
public final int getAndIncrement()//获取当前的值,并自增
public final int getAndDecrement()//获取当前的值,并自减
public final int getAndAdd(int delta)//获取当前的值,并加上预期的值
boolean compareAndSet(int expect,int update)//如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)

2.1.2 case(不推荐)

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyNumber2{
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

public class AtomicIntegerDemo {
    public static final int SIZE=50;

    public static void main(String[] args) throws InterruptedException{
        MyNumber2 myNumber2 = new MyNumber2();
        for (int i = 0; i < SIZE; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myNumber2.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        //等待50个线程计算完成后再获得最终值,否则在上面正在计算的时候main线程就去看结果,造成看到的结果不太准确。但是实际中不能这么写
        try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
        System.out.println(Thread.currentThread().getName()+"\t"+"result:"+myNumber2.atomicInteger.get());
    }
}

2.1.3 CountDownLatch优化上面case

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

class MyNumber2{
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

public class AtomicIntegerDemo {
    public static final int SIZE=50;

    public static void main(String[] args) throws InterruptedException{
        MyNumber2 myNumber2 = new MyNumber2();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (int i = 0; i < SIZE; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 1000; j++) {
                        myNumber2.addPlusPlus();
                    }
                } finally {
                    //完成一个减一个
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        //等待50个线程计算完成后再获得最终值,否则在上面正在计算的时候main线程就去看结果,造成看到的结果不太准确。但是实际中不能这么写
//        try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}

        //组织main线程获取数据,必须等到CountDownLatch的50个线程跑完
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t"+"result:"+myNumber2.atomicInteger.get());
    }
}

2.2 数组类型原子类

AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray

2.2.1 case

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
//        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
//        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
        for (int i = 0; i < atomicIntegerArray.length(); i++) {
            System.out.println(atomicIntegerArray.get(i));
        }
        System.out.println();
        
        int tmpInt = 0;
        tmpInt = atomicIntegerArray.getAndSet(0,1122);
        System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));

        tmpInt = atomicIntegerArray.getAndIncrement(0);
        System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));

    }
}

2.3 引用类型原子类

2.3.1 AtomicReference,(前面的自旋锁Demo)

2.3.2 AtomicStampedReference(前面的ABADemo)

携带版本号的引用类型原子类,可以解决ABA问题
解决多次的问题

2.3.3 AtomicMarkableReference

原子更新是否带有标记为的引用类型对象
解决一次性的问题,是否修改过。它的定义就是将状态戳简化为true|false

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;

public class AtomicMarkableReferenceDemo {
    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);

    public static void main(String[] args) {
        new Thread(()->{
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"默认标识"+"marked");
            //等待t2线程和t1拿到一样的标识,都是false
            try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            markableReference.compareAndSet(100,1000,marked,!marked);
        },"t1").start();

        new Thread(()->{
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName()+"\t"+"默认标识"+"marked");
            try {TimeUnit.MILLISECONDS.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
            boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
            System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CASresult:"+b);
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
        },"t2").start();
    }
}

2.4 对象的属性修改原子类

AtomicLntegerFieldUpdater:原子更新对象中int类型字段的值
AtomicLongFieldUpdater:原子更新对象中Long类型字段的值
AtomicReferenceFieldUpdater:原子更新引用类型字段的值
使用目的:以一种线程安全的方式操作非线程安全对象的某些字段
使用要求:
1.更新的对象属性必须使用public volatile修饰符
2.因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
面试问题:你在哪里用了volatile?:AtomicReferenceFieldUpdater

2.4.1 AtomicIntegerFieldUpdaterDemo(针对字段是int类型的)

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

class BankAccount{
    String bankName="ccb";
    //更新的对象属性必须使用public volatile修饰符
    public volatile int money=0;//钱数
    public synchronized void add(){
        money++;
    }
    //因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须
    //使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
    //不加synchronized,保证高性能原子性,只影响到变量
    public void transMoney(BankAccount bankAccount){
        fieldUpdater.getAndIncrement(bankAccount);
    }
}

/**
 * 以一种线程安全的方式操作非线程安全对象的某些字段
 * 需求:
 * 10个线程
 * 每个线程转账1000
 * 不使用synchronized,尝试使用AtomicIntegerFieldUpdater来实现
 */
public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) throws InterruptedException {
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=1000 ; j++) {
//                        bankAccount.add();
                        bankAccount.transMoney(bankAccount);
                    }
                } finally {
                    countDownLatch.countDown();

                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"\t"+"result:"+bankAccount.money);
    }
}

2.4.2 AtomicReferenceFieldUpdater(其他类型的字段)

package com.atguigu.springcloud.util.interrup;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

class MyVar{
    public volatile Boolean isInit = Boolean.FALSE;
    AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");
    public void init(MyVar myVar){
        if(referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)){
            System.out.println(Thread.currentThread().getName()+"\t"+"----start init,need 3seconds");
            try {TimeUnit.MILLISECONDS.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"\t"+"----over init");
        }else{
            System.out.println(Thread.currentThread().getName()+"\t"+"----已经有线程在进行初始化工作");
        }

    }
}
/**
 * 需求:
 * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,
 * 要求只能被初始化一次,只有一个线程操作成功
 */
public class AtomicReferenceFieldUpdaterDemo {
    public static void main(String[] args) {
        MyVar myVar = new MyVar();
        for (int i = 1; i <=5 ; i++) {
            new Thread(()->{
                myVar.init(myVar);
            },String.valueOf(i)).start();
        }
    }
}

2.5 原子操作增强类原理深度解析

DoubleAccumulator,DoubleAdder,LongAccumulator,LongAdder
在这里插入图片描述

2.5.1 点赞计数器

常用API
在这里插入图片描述
LongAdder只能用来计算加法,并且从零开始计算
LongAccumulator提供了自定义的函数操作
LongAdderAPIDemo

package com.atguigu.springcloud.util.interrup;

import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.LongBinaryOperator;

public class LongAdderAPIDEmo {
    public static void main(String[] args) {

        LongAdder longAdder = new LongAdder();
        longAdder.increment();
        longAdder.increment();
        longAdder.increment();
        System.out.println(longAdder.sum());


/*        LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() {
            @Override
            public long applyAsLong(long left, long right) {
                return left+right;
            }
        },0);//从0开始与下面的写法功能一样*/
        LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
        longAccumulator.accumulate(1);//1
        longAccumulator.accumulate(3);//4
        System.out.println(longAccumulator.get());
    }
}

2.5.2 性能评估

package com.atguigu.springcloud.util.interrup;

import lombok.SneakyThrows;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

class ClickNumber{
    int number = 0;
    //第一种传统 synchronized
    public synchronized  void clickBySynchronized(){
        number++;
    }
    //第二种 AtomicLong
    AtomicLong atomicLong = new AtomicLong(0);
    public void clickByAtomicLong(){
        atomicLong.getAndIncrement();
    }
    //第三种 LongAdder
    LongAdder longAdder = new LongAdder();
    public void clickByLongAdder(){
        longAdder.increment();
    }
    //第四种 LongAccumulator
    LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
    public void clickByLongAccumulator(){
        longAccumulator.accumulate(1);
    }
}
/**
 * 需求:50个线程,每个线程100w次,总点赞数出来
 */
public class AccumulatorCompareDemo {
    public static final int _1w = 10000;
    public static final int threadNumber=50;

    @SneakyThrows
    public static void main(String[] args) {
        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;

        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"毫秒clickBySynchronized"+clickNumber.number);


        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickByAtomicLong();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"毫秒clickByAtomicLong"+clickNumber.atomicLong.get());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"clickByLongAdder"+clickNumber.longAdder.sum());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNumber ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=100*_1w ; j++) {
                        clickNumber.clickByLongAccumulator();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"\t"+"clickByLongAccumulator"+clickNumber.longAccumulator.get());
    }
}

在这里插入图片描述

2.5.3 原理、源码分析

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

2.5.3.1 Striped64

Striped64有几个比较重要的成员函数
在这里插入图片描述

2.5.3.2 Striped64中一些变量或者方法的定义

在这里插入图片描述

2.5.3.3 Cell单元格类

是java.util.concurrent.atomic 下Striped64的一个内部类
在这里插入图片描述

2.5.3.4 LongAdder为什么这么快

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

2.5.3.5 源码分析

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

2.5.3.5.1 longAdder.increment()

add(L)
当线程变多的情况下第二个红框 !casBase(b = base, b+x)的cas操作就有可能没有抢到而返回了false,!false的情况下就进入了if逻辑块中,默认情况下uncontended变量为true(没有冲突)会进入longAccumulate(x,null,uncontended)方法。
278:新建Cells数组模式2大小
红色框代码(a=as[getProbe() & m])判断槽位是否有值
最后一个红框cas操作失败的情况了2槽位也顶不住就会继续执行longAccumulate扩容
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.5.3.5.2 longAccumulate

在这里插入图片描述

在这里插入图片描述

2.5.3.5.3 线程hash值:probe

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

2.5.3.5.4 总纲

在这里插入图片描述
阅读源码顺序简易2,3,1.
在这里插入图片描述

2.5.3.5.5 计算

CASE2
在这里插入图片描述
CASE3
在这里插入图片描述
CASE1
第一个if
在这里插入图片描述
第二个if
在这里插入图片描述

第三个if
在这里插入图片描述

第四个if
在这里插入图片描述

第五个if
在这里插入图片描述

第六个if
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sum
sum()会将所有Cell数组中的value和base累加作为返回值。
核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中取,从而降低更新热点
在这里插入图片描述
在这里插入图片描述

3 总结

AtomicLong
线程安全,可允许一些性能损耗,要求高精度可使用
保证精度,性能代价
AtomicLong是多个线程针对单个热点值Value进行原子操作
LongAdder
当需要在高并发下有较好的性能表现,且对值得精确度要求不高时,可以使用
保证性能,精度代价
LongAdder是每个线程拥有自己的槽,各个线程一般只对自己槽中的那个值进行CAS操作

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

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

相关文章

ABB IW93 HESG440356R1处理器模块

处理能力&#xff1a; IW93处理器模块具有强大的处理能力&#xff0c;能够高效执行控制逻辑和算法&#xff0c;确保工业过程的顺利运行。 通信接口&#xff1a; 该模块通常具有多种通信接口&#xff0c;用于与其他设备和系统进行数据交换和集成。这些接口可能包括以太网、串行…

9.13号作业

1> 将之前定义的栈类和队列类都实现成模板类 栈的模块类 #include <iostream> using namespace std;template <typename T> class Stack { private:T data[40]{0};T top-1; public:Stack (){cout<<"这是构造函数"<<endl;}int stack_e…

相亲App定制开发,助力您打造独特交友平台

作为专业的App开发团队&#xff0c;我们致力于为您打造一款独特而出色的相亲交友App。我们深刻理解相亲领域的需求&#xff0c;通过技术创新和用户体验优化&#xff0c;为您提供个性化的定制开发服务。 在相亲App的定制开发过程中&#xff0c;我们将全面考虑您的目标市场和用户…

Java项目---图片服务器

图片服务器--->服务器&#xff08;图床&#xff09; 核心功能&#xff1a;上传图片、展示图片等 比如&#xff1a;编写博客时我们会插入图片&#xff0c;本质上是往文章中放了一个链接&#xff08;URL&#xff09;&#xff0c;这个URL资源在另外一个服务器上。 核心知识点…

在ubuntu18.04上编译C++版本jsoncpp/opencv/onnxruntime且如何配置CMakelist把他们用起来~

这篇文章背景是笔者在ubuntu上编译C代码&#xff0c;依赖一些包&#xff0c;然后需要编译并配置到CMakelist做的笔记。主要也是一直不太懂CMakellist&#xff0c;做个笔记以防忘记&#xff0c;也给读者提供一站式的参考&#xff0c;可能您需要的不是这几个包&#xff0c;但大同…

STM32f103入门(12)USART串口信息发送+接收

USART 介绍串口发送使用工具初始化发送数据接收数据 介绍 电平标准是数据1和数据0的表达方式&#xff0c;是传输线缆中人为规定的电压与数据的对应关系&#xff0c;串口常用的电平标准有如下三种&#xff1a; TTL电平&#xff1a;3.3V或5V表示1&#xff0c;0V表示0 RS232电平&…

成都都市圈公共图书馆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著

成都都市圈公共图书馆《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著

Discrod账号为什么被封?怎么解封?

Discord作为海外社交产品的新晋王者&#xff0c;近两年来非常受欢迎&#xff0c;据统计&#xff0c;每个月使用Discord的用户数超过3000万。而在跨境电商领域&#xff0c;”内容社群”的打法已经见怪不怪&#xff0c;营销推广少不了Discord&#xff0c;拥有一个或者多个成熟的D…

深度学习-激活函数

文章目录 基础知识Sigmoid函数 &#xff08;Logistic函数&#xff09;双曲正切函数&#xff08;Tanh函数&#xff09;线性整流函数&#xff08;ReLU函数&#xff09;Leaky ReLU函数Softmax函数 基础知识 激活函数是神经网络中的一种非线性函数&#xff0c;它作为神经元的输出函…

Linux 安装Harbor镜像仓库私服

参考链接 Docker 的基础知识、安装、使用Harbor镜像仓库私服搭建 Harbor是什么&#xff1f; Harbor是由VMware公司开源的企业级的Docker Registry管理项目&#xff0c;它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。 Harbor 的所有组…

WebGL正射投影

目录 可视范围&#xff08;正射类型&#xff09; 可视空间 正射投影的盒状可视空间的工作原理 盒状可视空间 定义盒状可视空间 Matrix4.setOrtho&#xff08;&#xff09; 按键控制near、far ​编辑 示例效果 示例代码 代码详解 修改near和far值 通过右方向键增大n…

从物理叠加到化学反应,看方太如何把洗碗机玩出「新价值」

文 | 智能相对论 作者 | 佘凯文 随着酷暑烈日的逐渐远去&#xff0c;秋意也开始浓厚了起来。所谓“秋风起&#xff0c;秋膘贴”&#xff0c;在“金九银十”的当下&#xff0c;美食自是不可辜负的恩赐。肥美多膏的大闸蟹、软糯香甜的南瓜羹、爽脆可口的莲藕汤..... 秋季的美食…

idea中启动maven项目报错-java: 程序包lombok.extern.slf4j不存在问题如何解决

1、 现象&#xff1a; 在springboot的maven项目启动时&#xff0c;报错&#xff1a; Error:(3, 27) java: 程序包lombok.extern.slf4j不存在 编译不报错&#xff0c;maven依赖也合适&#xff0c;项目就是无法启动 原因&#xff1a; 其实不是项目本身或者maven本身的问题&am…

Java垃圾收集机制

目录 前言 判断对象是否存活 引用计数算法 可达性分析算法 GC Root的产生 Java中的四种引用类型 1.强引用 强引用弱化方式 方式1&#xff1a;使对象指向null 方式2&#xff1a;使对象超出作用域范围 2.软引用 3.弱引用 4.虚引用 垃圾收集算法 分代收集理论 垃圾…

Linux内核及可加载内核模块编程

图1 Linux系统整体结构 图2 Linux的源代码结构 下面显示一段内核模块代码案例&#xff1a; #include <linux/moduLe.h> #include <linux/kernel.h #include <linux/intt.h> /*模块的初始化函数lkp_ init()_init是用于初始化的修饰符 */ static int __init lk…

第十五届全国大学生数学竞赛报名快要截止了,你报上名了吗?

关于组织参加 第十五届全国大学生数学竞赛的通知 01 为了培养人才、服务教学、提高大学生学习数学的兴趣&#xff0c;培养学生分析问题、解决问题的能力&#xff0c;发现和选拔数学创新人才&#xff0c;为学生提供一个展示基础知识和思维能力的舞台&#xff0c;我校决定组织参…

SSM整合demo及个人思考

SSM整合 项目整体架构说明1. 创建Maven项目2. 配置web.xml4. 配置springmvc.xml5. 配置spring.xml6. 配置mybatis-config.xml以及创建mapper接口和mapper配置文件7. 配置log4j.xml8. 后端CURD测试8.1 在数据库中插入数据8.2 pojo中的实体类Employee8.3 mapper层的EmployMapper接…

oppo手机便签隐藏了一条怎样打开?手机如何找到隐藏便签?

有不少用户在使用OPPO手机的过程中&#xff0c;遇到了一些问题&#xff0c;例如自己在使用手机系统便签应用时&#xff0c;把一条重要的便签设置了隐藏&#xff0c;但是现在找不到隐藏的便签了。 那么oppo手机便签隐藏了一条怎样打开&#xff1f;OPPO手机如何找到隐藏便签&…

【C++】基础知识点回顾 上:命名空间与输入输出

前言 学习C一段时间后&#xff0c;再回过头来看这些C的基础知识&#xff0c;感觉有很多细节是自己当时没有注意的&#xff0c;所以写一篇文章来回顾复习一下C的基础知识。 命名空间的使用 相信很多朋友在学习C的第一个代码的时候&#xff0c;在写上头文件之后&#xff0c;紧…

运营商大数据实时获取精准数据

随着大数据技术的快速发展和完善&#xff0c;出现了一种新的扩张方式——互联网大数据的精准扩张。如果没有一个好的渠道来获得顾客&#xff0c;这就像准备热情地做饭&#xff0c;但当饭吃完后&#xff0c;人们只能饿了。 今天的消费者已经从最早的线下消费逐渐过渡到互联网消…