JUC大揭秘:从ConcurrentHashMap到线程池,玩转Java并发编程!
目录
JUC实现类
ConcurrentHashMap
回顾HashMap
ConcurrentHashMap
CopyOnWriteArrayList
回顾ArrayList
CopyOnWriteArrayList:
CopyOnWriteArraySet
辅助类 CountDownLatch
线程池
线程池
线程池优点
ThreadPoolExecutor
构造器各个参数含义:
线程池的执行
线程池中的队列
线程池中的拒绝策略
execute和submit的区别、
关闭线程池
ThreadLocal
原理分析
编辑
对象四种引用
ThreadLocal内存泄漏问题
JUC实现类
Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容 器的性能。
ConcurrentHashMap
回顾HashMap
双列集合 实现Map接口
键值对
键不能重复,值可以重复
只能存储一个为null的键
键是无序的
是线程不安全的.
HashMap不能有多个线程同时操作 ,如果有,则会抛出java.util.ConcurrentModificationException(并发修改异常)
键是如何判断是否重复
hashCode() 和 equals()
用到的一些结构
1.哈希表 默认长度是16 哈希每次扩容为原来的2倍 哈希表的负载因子为0.75
2.链表 链表长度>= 8 且 哈希表长度大于等于64 才会把链表转为红黑树 否则会先扩容哈希表 3.红黑树
讲讲添加一个元素的过程
HashMap不能再多线程场景下使用,否则会报异常
线程安全 : Hashtable 给操作的方法都添加了synchronized 但是效并发率低了
package com.ffyc.javaPro.thread.juc;import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;public class HashMapDemo {public static void main(String[] args) {HashMap<String,String> map = new HashMap<>();//模拟多线程场景for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {map.put(Thread.currentThread().getName(),"aaaa");System.out.println(map); }}.start();}}
}

package com.ffyc.javaPro.thread.juc;import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;public class HashMapDemo {public static void main(String[] args) {Hashtable<String,String> map = new Hashtable<>();//模拟多线程场景for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {map.put(Thread.currentThread().getName(),"aaaa");System.out.println(map);}}.start();}}
}
ConcurrentHashMap
ConcurrentHashMap也是线程安全的,但是与Hashtable实现线程安全的方式不同,他没有直接给方法加锁,
给哈希表的每一个位置加锁,将锁的粒度细化了,提高了并发效率.
如何细化锁: 不使用专门的分段锁了,而是采用每一个位置上的第一个节点Node对象,作为锁对象
使用CAS+synchronized实现线程安全
当哈希表的某个位置上还没有Node对象时,如果此时有多个线程操作,采用cas机制进行比较判断
如果某个位置上已经有了Node对象,那么直接使用Node对象作为锁即可ConcurrentHashMap 和Hashtable 都不能存储为null的键和为null值
为了消除歧义 因为他们都是在多线程场景下使用的,返回null时,不能分辨出时key的值为null,还是没有这个key,返回的null


package com.ffyc.javaPro.thread.juc;import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;public class HashMapDemo {public static void main(String[] args) {ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();//模拟多线程场景for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {System.out.println(map.get("a")); //值为null 还是没有这个键}}.start();}}
}

CopyOnWriteArrayList
回顾ArrayList
ArrayList 数组列表 线程不安全的
Vector 数组列表 线程安全的public synchronized boolean add(E e) 直接给方法加锁,效率低
public synchronized E get(int index) { get方法也加了锁,如果只有多个线程读操作,也只能一个一个读,效率低了
package com.ffyc.javaPro.thread.juc;import java.util.ArrayList;public class CopyOnWriteArrayListDemo {public static void main(String[] args) {ArrayList arrayList = new ArrayList();for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {arrayList.add("aaa");System.out.println(arrayList);}}.start();}}
}

import java.util.Vector;public class CopyOnWriteArrayListDemo {public static void main(String[] args) {Vector arrayList = new Vector();for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {arrayList.add("aaa");arrayList.get(i);System.out.println(arrayList);}}.start();}}
}
不报错,但是效率低
CopyOnWriteArrayList:
将读写并发效率进一步提升了.
读操作(get())是完全不加锁的,
只给能改变数据的方法(add,set,remove)进行了加锁,而且为了操作时,不影响读操作,
操作前现将数组进行拷贝,在副本上修改,修改之后,将副本重新赋值到底层数组.做到了只有写写是互斥的, 读写,读读都不互斥
适用于,读操作多,写操作少场景

import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListDemo {public static void main(String[] args) {CopyOnWriteArrayList arrayList = new CopyOnWriteArrayList();for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {arrayList.add("aaa");arrayList.get(i);System.out.println(arrayList);}}.start();}}
}
CopyOnWriteArraySet
CopyOnWriteArraySet 的实现基于 CopyOnWriteArrayList,不能存储重复数 据。
import java.util.concurrent.CopyOnWriteArraySet;public class CopyOnWriteArrayListDemo {public static void main(String[] args) {CopyOnWriteArraySet arrayList = new CopyOnWriteArraySet();for (int i = 0; i <100000 ; i++) {new Thread(){@Overridepublic void run() {arrayList.add("aaa");arrayList.set()arrayList.get(i);System.out.println(arrayList);}}.start();}}
}

辅助类 CountDownLatch
CountDownLatch 辅助类 递减计数器
使一个线程 等待其他线程执行结束后再执行
相当于一个线程计数器,是一个递减的计数器
先指定一个数量,当有一个线程执行结束后就减一 直到为0 关闭计数器
这样线程就可以执行了
package com.ffyc.javaPro.thread.juc;import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {CountDownLatch downLatch = new CountDownLatch(6);//计数for (int i = 0; i <6 ; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName());downLatch.countDown();//计数器减一操作}).start();}downLatch.await();//关闭计数System.out.println("main线程执行");}
}

线程池
字符串常量池
string a = "abc";
string b = "abc";
a==b;//true
IntegerCache.cache -128---+127进行缓存 自动装箱
数据库连接池 未来避免重复创建连接对象和销毁连接对象,实现创建若干个连接对象
使用jdbc
package com.ffyc.javaPro.thread.dbconnection.jdbcdemo;import org.junit.Test;import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;public class TestJDBC {@Testpublic void test() throws SQLException {Date date1 = new Date();for (int i = 0; i < 5000; i++) {Connection connection = JdbcUtil.getConnection();System.out.println(connection);JdbcUtil.close(connection);}Date date2 = new Date();System.out.println(date2.getTime()-date1.getTime());//23476}
}
使用阿里巴巴数据源
import org.junit.Test;import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;public class TestDruid {@Testpublic void test() throws SQLException {Date date1 = new Date();for (int i = 0; i < 5000; i++) {Connection connection = DruidUtil.getConnection();System.out.println(connection);DruidUtil.close(connection);}Date date2 = new Date();System.out.println(date2.getTime()-date1.getTime());//851}
}
数据库连接池
public class DataConfig {static final String URL = "jdbc:mysql://127.0.0.1:3306/dormdb?serverTimezone=Asia/Shanghai";static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";static final String JDBC_USER_NAME = "root";static final String JDBC_PASSWORD = "root";static final int POOL_SIZE = 10;}
封装Connection
数据库连接管道,就是对JDBC Connection进行封装而已,但是需要注意useState的这个标示。
连接池中的关闭连接实际上是将连接放回到连接池中以便其他使用者复用,实际上只是标示的改变而已
package com.ffyc.javaPro.thread.dbconnection.myconnectionpool;import java.sql.Connection;public class MyConnection{private Connection connection;//接收一个真正的连接对象private boolean state = false; //false-未使用, true-使用public MyConnection(Connection connection,boolean state) {this.connection = connection;this.state = state;}/*** 关闭连接,本质是修改标识*/public void close() {this.state = false;}public boolean getState() {return state;}public void setState(boolean state) {this.state = state;}
}
package com.ffyc.javaPro.thread.dbconnection.myconnectionpool;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;/*** 自定义连接池对象*/
public class MyConnectionPool {/*连接池容器*/private Vector<MyConnection> myConnections = new Vector();// 数据库驱动private String jdbcDriver;// 数据库访问地址private String jdbcURL;// 数据库连接用户名private String jdbcUsername;// 数据库连接密码private String jdbcPassword;// 数据库连接池大小private int poolSize;/*构造方法,初始化整个数据库连接池*/public MyConnectionPool() {init();createMyPooledConnection();}/*初始化数据库连接信息*/private void init() {//默认初始化数据库信息,正常情况从配置文件读取过来, 初始化数量和最大数量由构造方法指定this.jdbcDriver = DataConfig.JDBC_DRIVER;this.jdbcURL = DataConfig.URL;this.jdbcUsername = DataConfig.JDBC_USER_NAME;this.jdbcPassword = DataConfig.JDBC_PASSWORD;this.poolSize = DataConfig.POOL_SIZE;// 加载数据库驱动程序try {Class.forName(this.jdbcDriver);System.out.println("驱动加载成功");} catch (ClassNotFoundException e) {System.out.print("驱动加载失败");e.printStackTrace();}}/*创建数据库连接池*/private void createMyPooledConnection() {//创建指定数量的数据库连接for (int i = 0; i < poolSize; ++i) {try {//创建连接对象Connection connection = DriverManager.getConnection(jdbcURL, jdbcUsername, jdbcPassword);//将连接对象封装到自定义连接对象中, 设置状态为未使用MyConnection myPooledConnection = new MyConnection(connection, false);//将连接对象添加到连接池中myConnections.add(myPooledConnection);} catch (Exception e) {e.printStackTrace();}}}//如果得不到操作管道,需要去创建管道!public synchronized MyConnection getMyPooledConnection() {MyConnection myPooledConnection = null;try {//从连接池中获取一个连接对象myPooledConnection = getRealConnectionFromPool();//如果获取为空,说明连接池没有空闲连接,则循环继续获得,直到获得一个连接对象while (myPooledConnection == null) {myPooledConnection = getRealConnectionFromPool();if(myPooledConnection!=null){return myPooledConnection;//获得到连接对象,直接返回,结束循环}}} catch (SQLException e) {e.printStackTrace();}return myPooledConnection;}/*** 真正执行从连接池中获取连接对象* @return* @throws SQLException*/private synchronized MyConnection getRealConnectionFromPool() throws SQLException {MyConnection myConnection = null;//循环连接池集合for (MyConnection connection : myConnections) {//如果状态为未使用if (!connection.getState()) {try {//将连接对象状态改为使用中connection.setState(true);myConnection = connection;//获取到连接对象} catch (Exception e) {e.printStackTrace();}return myConnection;}}return null;}}
package com.ffyc.javaPro.thread.dbconnection.myconnectionpool;import java.util.Date;public class Test {public static void main(String[] args) {MyConnectionPool myConnectionPool = new MyConnectionPool();//创建了自己的数据库连接池Date date1 = new Date();for (int i = 0; i < 5000; i++) {new Thread(()->{// 从连接池中申请获取一个连接MyConnection myConnection = myConnectionPool.getMyPooledConnection();System.out.println(myConnection);myConnection.close();}).start();}Date date2 = new Date();System.out.println(date2.getTime()-date1.getTime());//430}
}
线程池
有时,有许多任务需要执行,而且每个任务都比较短,这种场景下,需要大量创建线程,
这样依赖创建的开销就变大了
可以事先创建一部分线程,不销毁,有任务时提交给线程去执行,执行完后不结束线程,避免了频繁的创建线程
池就是一个缓冲,可以事先准备好一些数据,用的时候直接使用即可,提高效率
package com.ffyc.javaPro.thread.pool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;public class PoolDemo {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);executorService.submit(new Runnable() {//提交任务@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}});}
}
线程池优点
重复利用线程,降低线程创建和销毁带来的资源消耗
统一管理线程,线程的创建和销毁都由线程池进行管理
提高响应速度,线程创建已经完成,任务来到可直接处理,省去了创建时间
ThreadPoolExecutor
在jdk5之后,Java中就提供了线程池的实现类
Executors.newFixedThreadPool();
//阿里巴巴开发规约建议使用的
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
构造器各个参数含义:
corePoolSize:核心线程池数量 5 创建ThreadPoolExecutor对象后,其实线程数量为0,有任务到来时,才会创建新的线程放到线程,直到核心线程池数量达到设定的值
可以直接调用prestartAllCoreThreads()或者 prestartCoreThread(),创建线程对象后,就可以立即创建线程。
maximumPoolSize:线程池最大线程数 10
keepAliveTime:非核心线程池中的线程在没有任务执行时,保持空闲多久后销毁,时间到期后,可以销毁空闲的线程
unit:keepAliveTime时间单位
workQueue:一个阻塞队列,用来存放等待执行的任务
threadFactory:线程工厂,主要用来创建线程
handler:表示拒绝执行任务时的策略(拒绝策略)
线程池的执行

当任务到达时,首先在核心线程池创建线程任务,如果核心线程池未满,那么直接让核心线程池执行,如果核心线程池已经满了,那么就将任务存放到队列中,等待执行
当任务继续提交过来时,如果队列已经放满了,就看非核心线程池中的线程数量有没有达到最大线程数量
如果已经达到并且没有空闲的线程,那么就采取某种拒绝的策略
线程池中的队列
ArrayBlockingQueue 给定队列的数量
LinkedBlockingQueue
package com.ffyc.javaPro.thread.threadpool;public class MyTask implements Runnable {private int taskNum;public MyTask(int num) {this.taskNum = num;}@Overridepublic void run() {try {Thread.currentThread().sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"task "+taskNum+"执行完毕");}
}
package com.ffyc.javaPro.thread.threadpool;import java.util.ArrayList;
import java.util.concurrent.*;public class Test {public static void main(String[] args) {/*MyTask myTask1 = new MyTask(1);创建任务对象Thread thread = new Thread(myTask1);/./创建线程对象 提交任务thread.start(); 启动线程*//*通过线程池执行任务*/ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for(int i=1;i<8;i++){MyTask myTask = new MyTask(i);executor.execute(myTask);//添加任务到线程池}}
}

线程池中的拒绝策略
当线程池中线程和队列都已经装满时,继续到来的任务无法处理时,可以采取以下四种策略进行拒绝。
package com.ffyc.javaPro.thread.threadpool;import java.util.ArrayList;
import java.util.concurrent.*;public class Test {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for(int i=1;i<=8;i++){MyTask myTask = new MyTask(i);executor.execute(myTask);//添加任务到线程池}}
}
AbortPolicy 策略:直接抛出异常

CallerRunsPolicy 策略:让提交任务的线程去执行,例如main线程
DiscardOleddestPolicy 策略:丢弃等待时间最长的任务,将新来的任务添加进去
DiscardPolicy 策略:直接丢弃无法执行的任务

execute和submit的区别、
execute();提交任务 但是不能接收返回值
submit();提交任务 可以接收返回值
package com.ffyc.javaPro.thread.threadpool;import java.util.ArrayList;
import java.util.concurrent.*;public class Test {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for(int i=1;i<=8;i++){MyTask myTask = new MyTask(i);//executor.execute(myTask);//添加任务到线程池Future<?> submit = executor.submit(myTask);}}
}
关闭线程池
shutdow();关闭时,会把以及提交到线程池中的线程执行完
package com.ffyc.javaPro.thread.threadpool;import java.util.ArrayList;
import java.util.concurrent.*;public class Test {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for(int i=1;i<=8;i++){MyTask myTask = new MyTask(i);Future<?> submit = executor.submit(myTask);}executor.shutdown();}
}

shutdowNow();立即关闭线程池,为执行的线程也会被中断
package com.ffyc.javaPro.thread.threadpool;import java.util.ArrayList;
import java.util.concurrent.*;public class Test {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for(int i=1;i<=8;i++){MyTask myTask = new MyTask(i);Future<?> submit = executor.submit(myTask);}executor.shutdownNow();}
}

ThreadLocal
需求: 多个线程中都有一个属于自己的num, 而不是多个线程共用同一个num
package com.ffyc.javaPro.thread.threadlocal;public class Demo {static int num = 0;public static void main(String[] args) {new Thread(){@Overridepublic void run() {num++;System.out.println(num);}}.start();new Thread(){@Overridepublic void run() {num++;System.out.println(num);}}.start();}
}
创建一个ThreadLocal对象,用来为每个线程会复制保存一份变量,实现线程封闭
package com.ffyc.javaPro.thread.threadlocal;public class ThreadLocalDemo {private static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>(){@Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {new Thread(){@Overridepublic void run() {localNum.set(1);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}localNum.set(localNum.get()+10);System.out.println(Thread.currentThread().getName()+":"+localNum.get());//11}}.start();new Thread(){@Overridepublic void run() {localNum.set(3);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}localNum.set(localNum.get()+20);System.out.println(Thread.currentThread().getName()+":"+localNum.get());//23}}.start();System.out.println(Thread.currentThread().getName()+":"+localNum.get());//0}
}

原理分析
ThreadLocal为每一个线程提供变量副本

为每一个线程创建ThreadLocalMap对象,在ThreadLocalMap对象中存储线程自己的变量副本
对象四种引用
强引用
String s = new String();
String s1 = s; 有引用指向对象
s1=null; s=null;
软引用
被SoftReference对象管理的对象,在内存不够时,先不回收被SoftReference管理的对象,
先进行一次垃圾回收,当垃圾回收后,如果内存够用了,那就不会被SoftReference管理的对象,
如果回收后,内存还不够,那么就会回收被SoftReference管理的对象
SoftReference<byte[]> m = new SoftReference<>(new byte[10]);
弱引用
被WeakReference管理的对象, 只要遇到一次GC,就会被回收掉
WeakReference<String> m = new WeakReference<>(new String("我是弱引用"));
虚引用
ThreadLocal内存泄漏问题
ThreadLocal与弱引用WeakReference有关系,那么在垃圾回收时,会把键回收了,但是值还存在强引用,不能回收,造成内存泄漏问题
每次使用完 ThreadLocal 都调用它的 remove()方法清除数据。
new Thread(){@Overridepublic void run() {localNum.set(1);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}localNum.set(localNum.get()+10);localNum.remove();System.out.println(Thread.currentThread().getName()+":"+localNum.get());//11}}.start();相关文章:
JUC大揭秘:从ConcurrentHashMap到线程池,玩转Java并发编程!
目录 JUC实现类 ConcurrentHashMap 回顾HashMap ConcurrentHashMap CopyOnWriteArrayList 回顾ArrayList CopyOnWriteArrayList: CopyOnWriteArraySet 辅助类 CountDownLatch 线程池 线程池 线程池优点 ThreadPoolExecutor 构造器各个参数含义: 线程…...
4.3--入门知识扫盲,IPv4的头部报文解析,数据报分片,地址分类(包你看一遍全部记住)
IPv4协议:网络世界的快递包裹指南(附拆箱说明书) “IPv4就像一张明信片,既要写清楚地址,又要控制大小别超重” —— 某网络工程师的桌面铭牌 一、IPv4报头:快递面单的终极艺术 1.1 报头结构图(…...
苍穹外卖-阿里云OSS使用
第一步: package com.sky.properties;import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;Component ConfigurationProperties(prefix "sky.alioss") …...
SSL/TLS 和 SSH 区别
背景知识 对称加密算法 定义:对称加密算法是指加密和解密使用同一个密钥的加密方式。 加密过程:发送方用密钥加密数据,接收方用相同的密钥解密数据。 优点:对称加密算法通常比非对称加密算法更高效,适合处理大量数据…...
Vue生命周期_Vue生命周期钩子
一、生命周期介绍 每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。 在此过程中,它也会运行被称为生命周期钩子的函数,让…...
数据库设计实验(4)—— 数据更新实验
一、目的与要求 掌握用SQL语句实现数据的插入、修改和删除。 二、实验准备 1. 建立一个商店的数据库store,记录顾客及其购物情况,由下面三个表组成: 商品(商品号,商品名,单价,商品类别&#x…...
Apache DolphinScheduler:一个可视化大数据工作流调度平台
Apache DolphinScheduler(海豚调度)是一个分布式易扩展的可视化工作流任务调度开源系统,适用于企业级场景,提供了一个可视化操作任务、工作流和全生命周期数据处理过程的解决方案。 Apache DolphinScheduler 旨在解决复杂的大数据…...
再学:call与delegatecall、call转账 Bank合约
目录 1.call与delegatecall 2.transfer && call 3.若想内部传递abi编码 4.Bank合约 1.call与delegatecall call:切换上下文 delegatecall:不切换上下文 delegatecall可以理解为 A在调用B这个集成在A的方法 可升级合约,常用del…...
关于解决新版本spring项目请求测试接口返回406的问题
目录 一、问题产生 二、问题排查 (1)首先是打断点debug进行排查 (2)网上查找相关资料排查 (3)老项目测试 三、问题解决 一、问题产生 使用Apifox对后端发送请求进行接口测试时返回状态码406࿰…...
linux入侵排查_应急响应
1.实验目标 掌握linux系统中信息收集的方法 掌握linux系统中持久化操作方法及排查方式 掌握linux系统入侵排查思路 2.实验步骤 1.统计攻击者爆破次数 2.排查攻击者第一次使用恶意用户登录的时间 3.检查sudoer文件 4.排查计划任务 5.排查计划任务 6.排查恶意服务 7.排查…...
AI视频生成产品体验分享(第2趴):Vidu、Hailuo、Runway、Pika谁更胜一筹?
hi,大家,继上次体验完可灵、即梦和pixverse,今天打算从产品经理的角度再研究下Vidu、Hailuo、Runway、Pika这几款产品!欢迎加入讨论! 一、产品简介 1. Vidu:国产自研的「一致性标杆」 📌官网…...
R语言高效数据处理-自定义格式EXCEL数据输出
注:以下代码均为实际数据处理中的笔记摘录,所以很零散, 将就看吧,这一篇只是代表着我还在,所以可能用处不大,这一段时间都很煎熬! 在实际数据处理中为了提升效率,将Excel报表交付给…...
JavaScript基础-获取元素
在Web开发中,使用JavaScript动态地访问和操作网页上的元素是一项基本技能。通过获取页面上的特定元素,我们可以对其进行各种操作,比如修改内容、样式或属性等。本文将详细介绍几种获取DOM元素的方法,并探讨它们的特点及适用场景。…...
基于srpingboot高校智慧校园教学管理服务平台的设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
【小白向】Word|Word怎么给公式标号、调整公式字体和花括号对齐
【小白向】Word|Word怎么给公式标号、调整公式字体和花括号对齐 我的版本:Word 2021 如需快速查看关键步骤,请直接阅读标红部分。 如果遇到无法调整的情况,可以直接下载我的示例文档进行参考:花括号和其他的示例公式.…...
uniapp-x vue 特性
生命周期 在组合式API中,组件可以监听应用和页面的生命周期。但由于应用和页面都有onShow和onHide,导致重名。所以在组合式的组件中监听页面的显示隐藏,改为了onPageShow和onPageHide。 这个和uniapp不一样,uniapp自定义组件无法…...
js逆向-下载某音乐
首先点击播放音乐,会拿到这样一个数据包 查看参数两个参数都是加密的 返回包里面有一个url,url拿到访问发现就是音频链接 访问直接下载下来 要逆向这两个参数采用xhr断点 这里加上路径的一部分 发现这些参数都是加密的 往下跟栈&am…...
百度OCR调用记录
根据说明,调用测试 设置注册的API Key和Secret Key 调用类(官方文档中有) 这里改传入路径; 测试问题 1.{"error_code":110,"error_msg":"Access token invalid or no longer valid"} 查到说是 …...
GraphDPI:通过互信息最大化进行图表示学习来消除部分标签歧义
论文源地址 1. 内容概要 本文提出了一种新的弱监督学习方法GraphDPI,解决部分标签学习(Partial Label Learning,PLL)中的标签歧义问题。GraphDPI结合了图表示学习和互信息最大化,通过图卷积网络(GCN&…...
项目实战:基于瑞萨RA6M5构建多节点OTA升级-创建系统最小框架<三>
MCUBoot项目创建完成后,接下来我们需要搭建多节点OTA系统最小框架,再将系统分模块搭建逐层完善,直到实现最终完整系统。开始动手干吧! 目录 一、创建项目 二、配置FSP 2.1 配置RS485属性 2.2 配置定时器0 2.3 创建初始化进程并配置属性 2.4 创建RS485进程并…...
C/C++模版初阶
文章目录 C/C模版初阶泛型编程函数模版函数模版概念函数模版格式函数模版的原理函数模版的实例化模版参数的匹配原则 类模版类模版的定义格式类模版的实例化 结语 我们今天又见面了,给生活加点<font colorred>impetus!!开启今天的编程之…...
1.FastAPI简介与安装
文章目录 为什么选择FastAPI?FastAPI支持的功能FastAPI的安装第一个FastAPI应用运行应用 为什么选择FastAPI? python web开发: Django: 适合大型复杂项目;Flask:适合灵活开发,搭建小型项目;FastAPI: 兼具开…...
重生之我在学Vue--第14天 Vue 3 国际化(i18n)实战指南
重生之我在学Vue–第14天 Vue 3 国际化(i18n)实战指南 文章目录 重生之我在学Vue--第14天 Vue 3 国际化(i18n)实战指南前言一、Vue I18n 核心配置1.1 基础环境搭建1.2 初始化配置1.3 全局挂载 二、多语言实现方案2.1 基础使用2.2 动态切换语言2.3 高级功能实现复数处理日期/货币…...
Java集合的底层原理
目录 Collection Arraylist HashSet 介绍 哈希值 哈希表的基本概念 HashSet 的内部实现 HashMap 哈希碰撞的处理 总结 TreeSet 特点 红黑树的特性 红黑规则 TreeSet 的内部实现 1. 存储结构 2. 添加元素(重点) 3. 查找元素 4. 删除元…...
SPI驱动(九) -- SPI_Master驱动程序
文章目录 参考资料:一、SPI传输概述二、SPI传输的两种方法2.1 旧方法2.2 新方法 参考资料: 参考资料: 参考内核源码: drivers\spi\spi.c 一、SPI传输概述 SPI控制器的作用是发起与它下面挂接的SPI设备之间的数据传输,那么控制…...
MySQL常用函数详解及SQL代码示例
MySQL常用函数详解及SQL代码示例 引言当前日期和时间函数字符串函数数学函数聚合函数结论 引言 MySQL作为一种广泛使用的关系型数据库管理系统,提供了丰富的内置函数来简化数据查询、处理和转换。掌握这些函数可以大大提高数据库操作的效率和准确性。本文将详细介绍…...
Linux 进程的创建、终止、等待与程序替换函数 保姆级讲解
目录 一、 进程创建 fork函数 二、进程的终止: 1. 想明白:终止是在做什么? 2.进程终止的3种情况? a.退出码是什么?存在原因?为什么int main()return 0? b.第三种进程终止的情况…...
大数据(1.1)纽约出租车大数据分析实战:从Hadoop到Azkaban的全链路解析与优化
目录 一、背景与数据价值 二、技术选型与组件分工 三、数据准备与预处理 四、实战步骤详解 1. 数据上传至HDFS 2. Hive数据建模与清洗 4.2.1 建表语句(分区表按年份): 4.2.2 数据清洗(剔除无效…...
BSCAN2-1:load design
1. DFT Flow Using Tessent Shell Tessent BoundaryScan 具有一个基本的高层次流程顺序。下图展示了将 Tessent BoundaryScan 插入设计所需的高层次步骤顺序。图中的每个步骤都链接到有关可测试性设计(DFT)流程的更详细信息,包括示例。 Desi…...
个人学习编程(3-18) leetcode刷题
爬楼梯: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1: 输入:n 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 …...
