【JavaEE初阶系列】——一万字带你了解 JUC常见类 以及 线程安全集合类(哈希表)

news2024/11/22 22:37:18

目录

🚩JUC(java.util.concurrent) 的常见类

🎈Callable 接口

🌈理解 Callable(相关面试题)

🌈理解 FutureTask 

📝线程创建方式

🎈 ReentrantLock可重入锁

🌈ReentrantLock 优势:

🌈ReentrantLock和synchronized区别:

🎈原子类

🎈信号量 Semaphore

🌈信号量代码示例

🎈CountDownLatch任务进度

🎈相关面试题

🚩线程安全的集合类  

🎈多线程环境使用 ArrayList

🎈多线程环境使用哈希表


🚩JUC(java.util.concurrent) 的常见类


🎈Callable 接口

Callable 是一个 interface . 相当于把线程封装了一个 " 返回值 ". 方便程序猿借助多线程的方式计算结果。Runnable不在意结果,而Callable接口,创建线程,可直接返回结果。
代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本
  • 创建一个类 Result , 包含一个 sum 表示最终结果, lock 表示线程同步使用的锁对象.
  • main 方法中先创建 Result 实例, 然后创建一个线程 t. 在线程内部计算 1 + 2 + 3 + ... + 1000.
  • 主线程同时使用 wait 等待线程 t 计算结束. (注意, 如果执行到 wait 之前, 线程 t 已经计算完了, 就不必等待了).
  • 当线程 t 计算完毕后, 通过 notify 唤醒主线程, 主线程再打印结果
//实现1+2+3.。。+1000
class Result{
    public  int sum;//创建
    public Object lock=new Object();
}
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Result result=new Result();//创建对象实例
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                int sum=0;
                for (int i = 1; i <=1000 ; i++) {
                    sum+=i;
                }
                synchronized (result.lock){
                    result.sum=sum;//将结果赋值给result类中sum
                    result.lock.notify();//然后唤醒
                }
            }
        });
        thread.start();
        synchronized (result.lock)
        {
            while (result.sum==0){
                result.lock.wait();//如果sum里的值一直是0,那么就一直等待
            }
            System.out.println(result.sum);//之后唤醒之后,就打印sum的值。
        }
    }
}
可以看到 , 上述代码需要一个辅助类 Result, 还需要使用一系列的加锁和 wait notify 操作 , 代码复
, 容易出错
代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本
  • 创建一个匿名内部类, 实现 Callable 接口. Callable 带有泛型参数. 泛型参数表示返回值的类型.
  • 重写 Callable 的 call 方法, 完成累加的过程. 直接通过返回值返回计算结果.
  • callable 实例使用 FutureTask 包装一下.
  • 创建线程, 线程的构造方法传入 FutureTask . 此时新线程就会执行 FutureTask 内部的 Callable call 方法, 完成计算. 计算结果就放到了 FutureTask 对象中.
  • 在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结.
package Java_improvr;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Callable_test2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer>callable=new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum=0;
                for (int i = 1; i <=1000 ; i++) {
                    sum+=i;
                }
                return sum;
            }
        };
    //将任务放进到线程中
        FutureTask<Integer>futureTask=new FutureTask<>(callable);
        Thread thread=new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
此处的get就能获取到Callable里面的返回结果,由于线程是并发执行的,
执行到主线程的get的时候,t线程可能还没执行完,没执行完的话,get就会阻塞。
    }
}

可以看到, 使用 Callable 和 FutureTask 之后, 代码简化了很多, 也不必手动写线程同步代码了 


🌈理解 Callable(相关面试题)

  • Callable 和 Runnable 相对, 都是描述一个 "任务". Callable 描述的是带有返回值的任务,Runnable 描述的是不带返回值的任务。
  • Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为 Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定.
FutureTask 就可以负责这个等待结果出来的工作.FutureTask对象就是放在线程中等待线程执行完后,调用get方法就得到了返回值。

🌈理解 FutureTask 

想象去吃麻辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你一张 "小票" . 这个小票就是
FutureTask. 后面我们可以随时凭这张小票去查看自己的这份麻辣烫做出来了没.做出来了get方法调用成功然后菜就出来了,如果没做出了,get方法就等待线程完成,直到出来后,就返回值。

📝线程创建方式

  1. 继承Thread,重写run(创建单独的类,也可以匿名内部类)
  2. 实现Runnable,重写run(创建单独的类,也可以匿名内部类)
  3. 实现Callable,重写call(创建单独的类,也可以匿名内部类)
  4. 实现lambda表达式
  5. ThreadFactory线程工厂
  6. 线程池

🎈 ReentrantLock可重入锁

可重入互斥锁. synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.

ReentrantLock 也是可重入锁. "Reentrant" 这个单词的原意就是 "可重入"
ReentrantLock 的用法:
  • lock(): 加锁, 如果获取不到锁就死等.
  • trylock(超时时间): 加锁, 如果获取不到锁, 等待一定的时间之后就放弃加锁.
  • unlock(): 解锁
ReentrantLock lock = new ReentrantLock(); 
-----------------------------------------
lock.lock();   
try {    
 // working    
} finally {    
 lock.unlock()    
}

我们之前学到try()..catch()..,如果加上finally的话,finally是肯定会被调用的,一般用于释放或者释放锁的作用,所以finally的话是肯定会执行到的代码。

🌈ReentrantLock 优势:

  • ReentrantLock,在加锁的时候,有俩种方式,lock,trylock
  • ReentrantLock提供了公平锁的实现(默认情况下是非公平锁)
  • ReentrantLock提供了更强大的等待通知机制,搭配了Condition类,实现等待通知的。

虽然ReentrantLock有上述的优势,但是咱们在加锁的时候,还是首选synchronized(而且synchronized背后还有一系列的优化手段,锁升级,锁粗化,锁消除),ReentrantLock使用更加的复杂,尤其是容易忘记解锁。

🌈ReentrantLock和synchronized区别:

  • 库的一个类, JVM 外实现的(基于 Java 实现). synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活, 但是也容易遗漏 unlock.
  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就 放弃.(trylock没有加锁就直接放弃,给了更多可操作的空间,放弃申请锁之后就可以做其他的事情。)
  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式。
// ReentrantLock 的构造方法
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • 更强大的唤醒机制. synchronized 是通过 Object wait / notify 实现等待-唤醒. 每次唤醒的是一 个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.(在多线程中等待的情况下,synchronized唤醒的是随机等待的线程,而ReentrantLock则唤醒的是指定的线程)。

如何选择使用哪个锁?
  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
  • 如果需要使用公平锁, 使用 ReentrantLock.

🎈原子类

在Java中,在java.util.Atomic包下有个,原子类(Atomic classes)是一组类,用于在多线程环境中进行原子操作。原子操作是不可分割的操作,可以看作是一组指令的执行过程中不会被中断的操作。原子类提供了一种线程安全的方式来执行诸如读取、写入和更新变量等操作,而无需显式地使用锁或同步块来保护数据。

Java中的原子类位于java.util.concurrent.atomic包中,主要有以下几个常见的原子类:

  1. AtomicBoolean: 提供了对boolean类型变量的原子操作。
  2. AtomicInteger: 提供了对int类型变量的原子操作。
  3. AtomicLong: 提供了对long类型变量的原子操作。
  4. AtomicReference: 提供了对引用类型变量的原子操作。
  5. AtomicIntegerArray: 提供了对int类型数组的原子操作。
  6. AtomicLongArray: 提供了对long类型数组的原子操作。
  7. AtomicReferenceArray: 提供了对引用类型数组的原子操作。

这些原子类通过使用底层的CAS(Compare-And-Swap)操作来实现原子性。CAS是一种乐观锁的机制,它在操作之前先比较内存中的值是否与预期值相等,如果相等,则执行操作,否则放弃操作。CAS操作是一种非阻塞算法,因此在高并发情况下性能通常比传统的基于锁的同步方式更好。


  AtomicInteger类举例,常见方法 

public class Test5 {
    public static AtomicInteger count=new AtomicInteger(0);//对int类型封装
    public static void main(String[] args) {
        count.addAndGet(1); //0+1=1         i+=delta
        System.out.println(count);
        count.decrementAndGet(); //--1=0 count=0  --i
        System.out.println(count);
        count.getAndIncrement(); //0++=0 count=1  i++
        System.out.println(count);
        count.incrementAndGet(); //++1=2          ++i
        System.out.println(count);
        count.getAndDecrement(); //2--=2 count=1  i--
        System.out.println(count);
    }
}
  • addAndGet(int delta);   i += delta;
  • decrementAndGet(); --i;
  • getAndDecrement(); i--;
  • incrementAndGet(); ++i;
  • getAndIncrement(); i++;

在多线程情况下,进行i++操作。这样就避免了 加锁,底层用的是CAS。 

package Java_improvr;

import java.util.concurrent.atomic.AtomicInteger;

public class Test1 {
    public static AtomicInteger count=new AtomicInteger(0);//对int类型封装
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count.getAndIncrement();
            }
        });

        Thread t2=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count.get());
    }
}

🎈信号量 Semaphore

信号量 , 用来表示 "可用资源的个数". 本质上就是一个计数器 .
理解信号量
可以把信号量想象成是停车场的展示牌,当前有车位100个,表示有100个可用资源。
  • 当有车开进去的时候,就相当于申请一个可用资源,可用车位就-1 (这个称为信号量的P操作)
  • 当有车开出去的时候,就相当于释放一个可用资源,可用车位就+1  (这个称为信号量的V操作
如果计数器的值已经为0了,还尝试申请资源,就会阻塞等待,直到有其他线程释放资源。
public class Test6 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore=new Semaphore(4);//申请了4个资源
       semaphore.acquire();
        System.out.println("P操作");
        semaphore.acquire();
        System.out.println("P操作");
        semaphore.acquire();
        System.out.println("P操作");
        semaphore.acquire();
        System.out.println("P操作");
        semaphore.acquire();
        System.out.println("P操作");
    }
}

这样我们就会让我们想到锁。

锁就是可用资源为1的信号量。(二元信号量)
  • 加锁操作,P操作,1->0
  • 解锁操作,V操作,0->1
操作系统,提供了信号量实现,提供了api,JVM封装了这样的api,就可以在java代码中使用了。

🌈信号量代码示例

  • 创建 Semaphore 示例, 初始化为 4, 表示有 4 个可用资源.
  • acquire 方法表示申请资源(P操作), release 方法表示释放资源(V操作)
  • 创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执行效果
package Java_improvr;

import java.util.concurrent.Semaphore;

public class Test6 {
    public static void main(String[] args) {
        Semaphore semaphore=new Semaphore(4);//申请了4个资源
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("申请资源");
                    semaphore.acquire();
                    System.out.println("我获取到了资源");
                    Thread.sleep(1000);
                    System.out.println("我释放了资源");
                    semaphore.release();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i <20 ; i++) {
            Thread t=new Thread(runnable);
            t.start();
        }
    }
}

🎈CountDownLatch任务进度

同时等待 N 个任务执行。适用于,多个线程来完成一系列任务的时候,用来衡量任务的进度是否完成,比如需要把一个大的任务,拆分成多个小的任务,让这些任务并发的去执行。
就可以 使用countDownLatch来判定说当前这些任务是否全都完成了。
比如说下载一个文件,就可以使用多线程下载。很多下载工具,下载速度,很一般,相比之下,有些专业的下载工具,就可以成倍的提升下载速度(IDM),多线程下载,(往往和资源服务器只有一个连接,服务器往往会对于连接传输的速度有限制),每个线程都建立一个连接,此时就需要把任务进行分割。
countDownLatch主要有俩个方法:
  • 1.await,调用的时候就会阻塞,就会等待其他的线程完成任务,所有的线程都完成了任务之后,此时这个await才会返回,才会继续往下走。
  • 2.countDown,告诉countDownatch,我当前这一个子任务已经完成了。

await代表所有任务完成,countDown代表分割成的小任务完成。

public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        //10个选手参赛,await就会在10次调用完countDown之后才能继续执行
        CountDownLatch latch=new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            int id=i;
            Thread t=new Thread(()->{
                System.out.println("thread"+id);
                latch.countDown();//代表一个小任务完成
            });
            t.start();
        }
        latch.await();//代表所有任务都完成
        System.out.println("await调用了代表所有任务都完成了");
    }
}

每个任务完成得顺序是随机得,就像10个选手参赛,选手就是线程,每个人得成绩都是不一样得,有先后区分。等到所有得选手都参赛完了后,就调用await方法。


🎈相关面试题

1) 线程同步的方式有哪些?
synchronized(锁), ReentrantLock(可重入锁), Semaphore(信号量) 等都可以用于线程同步.
2) 为什么有了 synchronized 还需要 juc 下的 lock?
以 juc 的 ReentrantLock 为例,
  • synchronized不用手动得释放锁,而ReentranLock需要手动得调用unlock释放锁,容易忽略。
  • synchronized申请锁失败后,就会死等,而ReentranLock可以通过trylock方式等待一段时间就会自动放弃。
  • synchronized是非公平锁,ReentranLock默认是非公平锁,可以通过构造方法传入一个true开启公平锁模式
  • synchronized搭配得wait/notify实现等待-唤醒线程,每次唤醒的是一个随机等待的线程。ReentranLock搭配的Condition类实现等待-唤醒,可以更精准控制唤醒某个指定的线程。
3) AtomicInteger 的实现原理是什么?
基于 CAS 机制. 伪代码如下
class AtomicInteger {
private int value;
public int getAndIncrement() {
     int oldValue = value;
     while ( CAS(value, oldValue, oldValue+1) != true) {
         oldValue = value;
     }
     return oldValue;
 }
}
4) 信号量听说过么?之前都用在过哪些场景下?
信号量"用来表示资源的个数",本质上是个计数器。
使用信号量可以实现"共享锁",比如某个资源允许3个线程同时使用,那么就可以使用P操作作为加锁,V操作作为解锁,前三个线程的P操作都能顺序返回,后续线程再进行P操作就会阻塞等待,直到前面的线程执行了V操作。

🚩线程安全的集合类  

原来的集合类 , 大部分都不是线程安全的 .
Vector, Stack, HashTable, 是线程安全的(不建议用), 其他的集合类不是线程安全的.
针对这些线程不安全的集合类,要想在多线程环境下使用,就需要考虑好,线程安全问题了。

🎈多线程环境使用 ArrayList

1) Collections.synchronizedList(new ArrayList);
synchronizedList 是标准库提供的一个基于 synchronized 进行线程同步的 List.
synchronizedList 的关键操作上都带有 synchronized.
这个操作会返回新的对象,这个新的对象就相当于给ArrayList套了一层壳,这层壳就是在方法上直接使用synchronized的。
2) 使用 CopyOnWriteArrayList
写时拷贝,比如俩个线程使用同一个ArrayList,可能会读,也可能会修改。
如果要是俩个线程读,就直接读就好了,
如果某个线程需要修改,就把ArrayList复制出一份副本,就修改这个副本,与此同时,另一个线程仍然可以读取数据(从原来的数据上进行读取)一旦这边修改完毕,就会使用修改好的这份数据,替代掉原来的数据(往往就是一个引用赋值)
CopyOnWrite容器即写时复制的容器
  • 当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy, 复制出一个新的容器,然后新的容器里添加元素,
  • 添加完元素之后,再将原容器的引用指向新的容器
这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
  • 优点:

在读多写少的场景下, 性能很高, 不需要加锁竞争.

  • 缺点:

1. 占用内存较多.

2. 新写的数据不能被第一时间读取到.

其实这种场景特别适合于 服务器的配置更新,可以通过配置文件,来描述配置的详细内容(本身就不会很大)配置的内容会被读到内存中,再由其他线程,读取到这里的内容,但是修改这个配置内容,往往只有一个线程去修改,使用某个命令让服务器重新加载配置,就可以使用写时拷贝的方式了。

🎈多线程环境使用哈希表

HashMap 本身不是线程安全的 .
在多线程环境下使用哈希表可以使用 :
  • Hashtable
  • ConcurrentHashMap
Hashtable保证线程安全,主要就是给关键方法,加上synchronized,直接加到方法上的 (相当于给this加锁),只要俩个线程,在操作同一个Hashtable就会出现锁冲突。
俩个不同key映射到同一个数组下标上,就会出现hash冲突,使用链表来解决hash冲突。
按照上述这样的方式来操作,并且在不考虑 触发扩容 的前提下,操作不同的链表的时候就是线程安全的,相比之下,如果俩个线程,操作的是同一个链表,才比较会出现问题。(就像之前说的,多线程对同一个变量进行修改,会出现线程安全问题)。
1》ConcurrentHashMap最核心的改进,就是把一个大局的大锁,改进成 每个链表独立的一把小锁,这样做,大幅度的降低了锁冲突的概率。    大锁-》小锁
如果一个大锁 ,当很多线程进行插入到哈希表的时候,此时每个线程都得等待前一个线程进入并且插入完成后,然后下一个线程进入,这就是造成了阻塞等待的状况。                              其实一个hash表中有很多这样的链表,俩个线程恰好同时访问同一个链表的情况,本身就很少。所以我们就想到                                                                                                                              

   将一个大局的大锁,改进成每个链表独立的一把小锁,锁对象就是链表的头结点。 遇到访问不同的链表的时候,是不产生锁冲突的,遇到访问同一个链表,会阻塞等待,但是比大锁更高效,因为这样其他所有的线程都被阻塞了,而小锁会避免不必要的锁冲突。            
分段锁:java8之前 ConcurrentHashMap就是基于分段锁(多个链表公用一把锁)的方式实现的。等到java8开始之后,就成了直接在链表头结点加锁的形式。

2》充分利用到了CAS特性,把一些不必要的加锁环节省略加锁了

    比如:需要使用变量记录hash表中的元素个数,此时 ,就可以使用原子操作(CAS) 修改元素个数。

3》ConcurrentHashMap还有一些激进的操作,针对读操作没有加锁。读和读之间,读和写之间,都不会有锁竞争。

写和写之间还是要加锁的。

4》ConcurrentHashMap 针对扩容操作,做出了单独的优化

本身Hashtable或者HashMap 在扩容的时候,都是需要把所有的元素都拷贝一遍的(如果元素很多,拷贝的就比较耗时)

用户访问100次,99次都很流畅,其中一次卡了(正好触发了扩容的机制,导致了出现卡顿)

化整为零。一旦需要扩容,确实需要搬运,不是在一次操作中搬运完成,而是分成多次来搬运,每次只搬运一部分数据,避免这单次操作过去卡顿。


我时常消极 又觉得生活美好。

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

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

相关文章

【Python BUG】ModuleNotFoundError: No module named ‘streamlit.cli‘

问题 streamlit做大模型前端demo&#xff0c;安装后不好使。 解决方案 参考&#xff1a; https://zhuanlan.zhihu.com/p/656164361 找到下面文件&#xff1a; 替换、修改内容&#xff1a; # from streamlit.cli import main from streamlit.web.cli import main原来是上边…

JavaScript_与html结合方式

JavaScript_语法 ECMAScript&#xff1a;客户端脚本语言的标准 1.基本语法 1.1 与html结合方式&#xff08;2种&#xff09; 1. 内部JS 定义<script>,标签体内容就是js代码 2. 外部JS 定义<script>,通过src属性引入外部的 js文件 注意&#xff1a; 1.<script>…

【DPU微知识】NVIDIA-BlueFiled DPU概念之:BFB是什么?

BFB是BlueField Boot Stream的缩写&#xff0c;由Bootloader、Linux OS、Romfs组成。本质&#xff1a;bootload、系统、文件系统。&#xff08;其实就是DPU的上装类比标准host的grub、linux、文件系统&#xff0c;类似做Linux移植时候构建的最小文件系统的三件套差不多&#xf…

3D模型格式转换工具HOOPS Exchange如何将3D文件加载到PRC数据结构中?

HOOPS Exchange是一款高效的数据访问工具&#xff0c;专为开发人员设计&#xff0c;用于在不同的CAD&#xff08;计算机辅助设计&#xff09;系统之间进行高保真的数据转换和交换。由Tech Soft 3D公司开发&#xff0c;它支持广泛的CAD文件格式&#xff0c;包括但不限于AutoCAD的…

uniapp项目-懂你找图

文章目录 项目介绍项目搭建1.项目创建 2.新增tabbar3引入字体图标 uni-ui介绍使用 uni-api介绍 首页模块功能分析搭建子页面分段器介绍 封装自己的异步请求为什么要封装封装的思路 编写首页-推荐页面分页功能 专辑列表获取专辑详情数据 项目介绍 微信小程序&#xff0c;提供图…

苹果开发者账号注册后生成开发证书和发布证书的流程解析

转载&#xff1a;注册苹果开发者账号的方法 在2020年以前&#xff0c;注册苹果开发者账号后&#xff0c;就可以生成证书。 但2020年后&#xff0c;因为注册苹果开发者账号需要使用Apple Developer app注册开发者账号&#xff0c;所以需要缴费才能创建ios证书了。 所以新政策出…

【机器学习】机器学习创建算法第3篇:K-近邻算法,学习目标【附代码文档】

机器学习&#xff08;算法篇&#xff09;完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;机器学习算法课程定位、目标&#xff0c;K-近邻算法定位,目标,学习目标,1 什么是K-近邻算法,1 Scikit-learn工具介绍,2 K-近邻算法API。K-近邻算法&#xff0c;1.4 …

EfficientVMamba实战:使用 EfficientVMamba实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

搜索二维矩阵 II - LeetCode 热题 21

大家好&#xff01;我是曾续缘&#x1f497; 今天是《LeetCode 热题 100》系列 发车第 21 天 矩阵第 4 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&…

PHP在线加密系统网站源码

源码介绍 PHP在线加密系统网站源码&#xff0c;这个是sg的加密,免费可用(目前)并不会收费 源码说明&#xff1a;下载直接上传即可 下载地址 蓝奏云下载&#xff1a;https://wfr.lanzout.com/i6c331togiji

关于磁盘算法

性能瓶颈&#xff1a;IO–>IO调度–>IO调度算法–>1楼到顶楼&#xff0c;再从零楼下来&#xff0c;效率高–>IO调度目标–>IO算法–>电梯算法–linux6和Linux7算法不一样–>linux6 单队列 Linux7 多队列 inux6: 试用于不同的环境和介质。 noop 适合闪存&…

jmeter性能压测

jvm指令 jstat -gcutil -h5 -t 1 3s 发压端的tcp这么达到1000TPS jmeter的jvm的设置

PW1503限流芯片:可达3A限流,保障USB电源管理安全高效

在电源管理领域&#xff0c;开关的性能直接关系到设备的稳定性和安全性。今天&#xff0c;我们将详细解析一款备受关注的超低RDS&#xff08;ON&#xff09;开关——PW1503。它不仅具有可编程的电流限制功能&#xff0c;还集成了多项保护机制&#xff0c;为各类电子设备提供了高…

springboot之MybatisPlus

文章目录 一、ORM二、mybatis实际操作三、mybatis-plus 一、ORM 简单来说ORM就是一个能够帮我们把java中Bean类映射到数据库中。 使用mybatis-plus。 配置架包 <!-- MyBatisPlus依赖 --><dependency><groupId>com.baomidou</groupId><art…

谷粒商城实战(008 缓存)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第151p-第p157的内容 简介 数据库承担落盘&#xff08;持久化&#xff09;工作 拿map做缓存 这种是本地缓存&#xff0c;会有一些问题 分布…

注解——自定义注解、元注解、开发Junite框架

官方注解 自定义的注解 元注解 指的是&#xff1a;修饰注解的注解 常用的两个元注解——Target\ Retention 注解的解析 应用场景&#xff1a;开发Junit框架

KH-IPEX-K501-29

KH-IPEX-K501-29品牌: kinghelm(金航标)封装: SMD 描述: 1代

Linux安装Weblogic保姆级教程

文章目录 前言一、Weblogic安装包下载二、安装JDK三、Weblogic安装1.创建Linux用户2.创建weblogic的安装目录3.上传weblogic的安装包4.解压缩5.修改 /opt/weblogic 目录的所有权6.创建 oraInst.loc 文件7.创建 wls.rsp 响应文件8.切换用户9.静默安装weblogic10.切换到root用户1…

【闲聊】-网页划词翻译插件

英文之痛 作为程序猿&#xff0c;常常需要接触外文网站&#xff0c;以前很痛苦&#xff0c;现在大模型时代有很多智能工具可以直接翻译&#xff0c;翻译的虽然越来越好&#xff0c;但是还是不如直接看英文能理解本义&#xff0c;相信我&#xff0c;看翻译的理解和看原文的理解…

Folder Icons for Mac v1.8 激活版文件夹个性化图标修改软件

Folder Icons for Mac是一款Mac OS平台上的文件夹图标修改软件&#xff0c;同时也是一款非常有意思的系统美化软件。这款软件的主要功能是可以将Mac的默认文件夹图标更改为非常漂亮有趣的个性化图标。 软件下载&#xff1a;Folder Icons for Mac v1.8 激活版 以下是这款软件的一…