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();

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

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

相关文章

C++之list类及模拟实现

目录 list的介绍 list的模拟实现 定义节点 有关遍历的重载运算符 list的操作实现 &#xff08;1&#xff09;构造函数 (2)拷贝构造函数 &#xff08;3&#xff09;赋值运算符重载函数 &#xff08;4&#xff09;析构函数和clear成员函数 &#xff08;5&#xff09;尾…

Elasticsearch 向量检索详解

文章目录 1、向量检索的用途2、适用场景2.1 自然语言处理&#xff08;NLP&#xff09;&#xff1a;2.2 图像搜索&#xff1a;2.3 推荐系统2.4 音视频搜索 3、向量检索的核心概念3.1 向量3.2 相似度计算3.3 向量索引 4、案例&#xff1a;基于文本的语义搜索5、总结 向量检索是 E…

自学软硬件第755 docker容器虚拟化技术

见字如面&#xff0c; 这里是AIGC创意人_竹相左边&#xff0c; 正在通过AI自学软硬件工程师&#xff0c;目标手搓可回收火箭玩具。 我很喜欢 《流浪地球 2》中 &#xff0c;马兆&#xff1a;没有硬件支撑&#xff0c;你破解个屁。 写作背景 今天在剪视频&#xff0c;然后看…

不可不知的分布式数据库-TiDB

不可不知的分布式数据库-TiDB 介绍TiDb架构TiDb与Mysql的区别功能特性性能表现数据可靠性运维管理成本 Docker部署TiDB1. 获取 TiDB 配置文件2. 启动 TiDB 集群3. 连接到 TiDB4. 停止和清理 TiDB 集群注意事项 实用案例TiDB实现分布式事务实现原理实现方式SQL 方式编程方式 注意…

20242817李臻《Linux⾼级编程实践》第四周

20242817李臻《Linux⾼级编程实践》第4周 一、AI对学习内容的总结 第5章 Linux进程管理 5.1 进程基本概念 进程与程序的区别 程序&#xff1a;静态的二进制文件&#xff08;如/bin/ls&#xff09;&#xff0c;存储在磁盘中&#xff0c;不占用运行资源。进程&#xff1a;程…

基于 Prometheus + Grafana 监控微服务和数据库

以下是基于 Prometheus Grafana 监控微服务和数据库的详细指南&#xff0c;包含架构设计、安装配置及验证步骤&#xff1a; 一、整体架构设计 二、监控微服务 1. 微服务指标暴露 Spring Boot 应用&#xff1a; xml <!-- 添加 Micrometer 依赖 --> <dependency>…

使用GoogleNet实现对花数据集的分类预测

使用GoogleNet实现对花数据集的分类预测 1.作者介绍2.关于理论方面的知识介绍2.1GooLeNet的知识介绍2.2CNN发展阶段2.2GooLeNet创新模块 3.关于实验过程的介绍&#xff0c;完整实验代码&#xff0c;测试结果3.1数据集介绍3.2实验过程3.3实验结果 1.作者介绍 王海博, 男 , 西安…

流量密码破解:eBay店铺首页改版后的黄金展示位

流量密码破解&#xff1a;eBay店铺首页改版后的黄金展示位 近年来&#xff0c;跨境电商行业竞争日趋激烈&#xff0c;流量分配机制的调整成为平台卖家最为关注的核心议题之一。作为全球领先的在线电商平台&#xff0c;eBay的每一次页面优化都可能对卖家的经营策略产生深远影响…

从0到1彻底掌握Trae:手把手带你实战开发AI Chatbot,提升开发效率的必备指南!

我正在参加Trae「超级体验官」创意实践征文&#xff0c; 本文所使用的 Trae 免费下载链接&#xff1a; www.trae.ai/?utm_source… 前言 大家好&#xff0c;我是小Q&#xff0c;字节跳动近期推出了一款 AI IDE—— Trae&#xff0c;由国人团队开发&#xff0c;并且限时免费体…

Netty:java高性能网络编程的基石(下)

一、Netty是什么&#xff1f;为什么需要它&#xff1f; Netty是一个异步事件驱动的网络应用框架&#xff0c;基于Java NIO技术封装&#xff0c;用于快速开发高性能、高可靠性的网络服务器和客户端程序。作为当前最流行的NIO框架之一&#xff0c;支撑着Dubbo、RocketMQ、Elasti…

leetcode-47.全排列II

如何在有重复值的时候节省时间是优化重点。 基础写法肯定是按无重复值时的全排列写&#xff0c;在其中要加上防止走重复路径的分支。 能防止的也只有同层&#xff0c;如果同层走一个值&#xff0c;但是该值重复&#xff0c;且走过了&#xff0c;则放弃走该分支。所以设layer_u…

函数(函数的概念、库函数、自定义函数、形参和实参、return语句、数组做函数参数、嵌套调用和链式访问、函数的声明和定义、static和extern)

一、函数的概念 •C语⾔中的函数&#xff1a;⼀个完成某项特定的任务的⼀⼩段代码 •函数又被翻译为子函数&#xff08;更准确&#xff09; •在C语⾔中我们⼀般会⻅到两类函数&#xff1a;库函数 ⾃定义函数 二、库函数 1 .标准库和头文件 •C语⾔的国际标准ANSIC规定了⼀…

孜然SEO静态页面生成系统V1.0

孜然SEO静态页面生成系统&#xff0c;1秒生成上万个不同的静态单页系统&#xff0c;支持URL裂变采集&#xff0c;采集的内容不会重复&#xff0c;因为程序系统自带AI重写算法&#xff0c;AI扩写算法&#xff0c;可视化的蜘蛛池系统让您更清楚的获取到信息&#xff01; 可插入二…

Secs/Gem第一讲 · 总结精华版(基于secs4net项目的ChatGpt介绍)

好的&#xff01;这就是《第一讲 总结精华版》——为背诵准备的口述速成稿&#xff0c;适合面试前复习答题用。我们会分为两个部分&#xff1a; 第一部分&#xff1a;一整段口述稿&#xff0c;可以当成面试时开口自我介绍用&#xff1b;第二部分&#xff1a;要点清单关键词串…

预处理指令中#if 和 #endif的用法

在 C 语言中&#xff0c;#if 和 #endif 是预处理指令&#xff0c;用于条件编译。它们的核心作用是&#xff1a;根据预处理器能够识别的条件&#xff08;通常是宏定义或常量表达式&#xff09;&#xff0c;决定某段代码是否参与编译。 — 基本功能 #if 用于开启一个条件编译块…

【数据库】掌握MySQL事务与锁机制-数据一致性的关键

在数据库的世界里&#xff0c;数据就是一切。而确保数据的准确性和一致性&#xff0c;则是数据库系统的核心任务之一。想象一下&#xff0c;如果没有合适的机制&#xff0c;当多个用户同时试图修改同一条数据时&#xff0c;会发生什么&#xff1f; chaos&#xff08;混乱&#…

linux 基础网络配置文件

使用“ifconfig”命令查看网络接口地址 直接执行“iconfg”命令后可以看到ens33、10、virbr0这3个网络接口的信息&#xff0c;具体命令如下 ifconfig ##查看网络接口地址 ens33:第一块以太网卡的名称 lo:“回环”网络接口 virbr0:虚拟网桥的连接接口 查看指…

OpenCV特征提取与深度学习CNN特征提取差异

一、特征生成方式 ‌OpenCV传统方法‌ ‌手工设计特征‌&#xff1a;依赖人工设计的算法&#xff08;如SIFT、FAST、BRIEF&#xff09;提取图像中的角点、边缘等低层次特征&#xff0c;需手动调整参数以适应不同场景‌。‌数学驱动‌&#xff1a;基于梯度变化、几何变换等数学规…

五种方案实现双链路可靠数据传输

本文介绍五种双链路数据传输方案,目标是利用设备的多个传输通道,(如双有线网口,网口+wifi, 网口+5G等场景 , 网口+ 自组网, 自组网 + 5G等),将数据复制后分流、分路同时传输,以期提高数据传输可靠性,满足高可靠性传输的应用场景需求。部分方案给出了实际验证结果 。 …

【备赛】遇到的小问题-1

问题描述-1 想实现的功能是&#xff0c;通过ADC实时测量某引脚的电压及其占空比。 可以通过旋转电位器&#xff0c;更改其电压。 首先我定义了这几个变量 uint32_t adc_value;//HAL库函数里面得出的采样值(实时更新) uint32_t percentage6;//占空比&#xff0c;随着adc_val…