JAVAEE-多线程(4)

news2025/1/16 2:44:53

目录

定时器

实现自己的Timer

线程池

常见的锁策略:

乐观锁和悲观锁 

读写锁和普通互斥锁

重量级锁和轻量锁

自旋锁和挂起等待锁

公平锁和非公平锁

可重入锁和不可重入锁

synchronized

CAS

CAS和ABA问题

锁粗化

 JUC

原子类

Semaphore

CountDownLatch

多线程使用队列和哈希表

ConcurrentHashMap


定时器

package threading;

import java.util.Timer;
import java.util.TimerTask;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-10
 * Time: 19:04
 */
public class L110 {
    public static void main(String[] args) {
        Timer timer =new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到,快起床");
            }
        },3000);
        System.out.println("开始计时");

        //第一个参数为安排的任务是啥(new timertask),第二个是多长时间之后来执行(3000)
        //timertask就是一个runnable,我们要做的即使继承timertask然后重写run方法,从而执行指定任务。
    }
}

然后输出结果就是,先打印开始计时,然后三秒后打印出时间到快起床

 

同时我们也可以用多组定时器来计时

package threading;

import java.util.Timer;
import java.util.TimerTask;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-10
 * Time: 19:04
 */
public class L110 {
    public static void main(String[] args) {
        Timer timer =new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到,快起床");
            }
        },3000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到2,快起床");
            }
        },4000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("时间到3,快起床");
            }
        },5000);
        System.out.println("开始计时");

        //第一个参数为安排的任务是啥(new timertask),第二个是多长时间之后来执行(3000)单位为毫秒
        //timertask就是一个runnable,我们要做的即使继承timertask然后重写run方法,从而执行指定任务。
    }
}

这里的进程没有退出是因为timer内部需要有一组线程来执行注册的任务,而这里的线程是前台线程,会影响进程退出。 

实现自己的Timer

 

package threading;

import java.util.PriorityQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 9:34
 */
class  Mytask implements Comparable<Mytask>{
    private Runnable runnable;
    //要执行什么任务

    private long time;
    //什么时间执行,是一个时间戳

    public Mytask(Runnable runnable,long delay){
        this.runnable = runnable;
        this.time=System.currentTimeMillis() + delay;
    }

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(Mytask o) {
        return (int) (this.time -o.time);
    }
}
class Mytimer{
    private BlockingQueue<Mytask> queue = new PriorityBlockingQueue<>();
    //使用阻塞队列来执行多个任务,因为优先级队列不是线程安全的,使用不使用

    private Object locker = new Object();
    public Mytimer(){
        //创建一个扫描线程,让这个线程不停的来检查队首元素,如果时间到了就执行任务
        Thread t = new Thread(()->{
            while(true){
                try {
                    synchronized (locker){
                        //检查首元素
                        Mytask task = queue.take();
                        //因为不管是设置时间刚好到系统时间,还是
                        //设置时间已经小于系统时间,都需要执行任务

                        long curtime = System.currentTimeMillis();
                        if(curtime>= task.getTime()){
                            //如果系统时间大于或者等于设置时间,就执行任务
                            task.getRunnable().run();
                        }
                        else {
                            //还没到点
                            queue.put(task);
                            //没到点就进行等待

                            locker.wait(task.getTime() - curtime);

                        }
                    }

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
    }
    public void schedule(Runnable runnable,long after) throws InterruptedException {
        Mytask mytask = new Mytask(runnable,after);
        queue.put(mytask);
        synchronized (locker){
            locker.notify();
        }
    }
}
public class L111 {
    public static void main(String[] args) throws InterruptedException {
        Mytimer timer = new Mytimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("时间到 ");
            }
        },3000);

        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("时间到2 ");
            }
        },4000);

        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("时间到3 ");
            }
        },5000);
        System.out.println("开始计时");
    }
}

线程池

package threading;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 10:37
 */
public class L1111 {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        //创建线程池,并没有显示的new去创建,而是通过另外executors类的静态方法newCachedThreadPool来完成的(工厂模式的工厂方法创建的)
        pool.submit(new Runnable() {
            //使用submit方法把任务提交到线程池中,线程池里面就会有一些线程来完成这里的任务2
            @Override
            public void run() {
                System.out.println("任务");
            }
        });
    }
}

执行多个任的线程池:

package threading;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 10:51
 */
class MyThreadPool{
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    public void  submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    public  MyThreadPool(int m){
        for (int i = 0; i < m; i++) {
            Thread t = new Thread(()->{
               while(true){
                   try {
                       Runnable runnable = queue.take();
                       runnable.run();
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
            });
        }
    }
}

public class L1112 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int taskid = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务; "+ taskid + "当前线程:" + Thread.currentThread().getName());
                }
            });
        }
    }
}

 线程池,标准库提供了一个ThreadPoolExecutor类

corePoolSize
核心线程,空闲了也不会销毁

maximunPoolSize
非核心线程,空闲了就销毁了

//这个设定是为了防止线程池空闲的时候,非核心线程数目太多

keepAliveTime
运行非核心线程空闲的最大时间上限

unit
时间的单位

workQueue
手动给线程池传入一个任务队列

//本来线程池有自己的任务队列,如果我们不传会自己创建的
//这个时候把这个队列的任务拷贝到线程池就多余,只要使用线程池本身有的队列就可以

threadFactory
描述了线程是怎么创建的,工厂对象就负责创建线程,程序员还可以手动指定线程的创建策略

RejectedExecutionHandler handler
线程池的拒绝策略,线程池的任务队列已经满了(工作线程忙不过来了) 但是还在继续添加新任务该怎么办

详细介绍

RejectedExecutionHandler handler

RejectedExecutionHandler handler又有:

ThreadPoolExecutor.AbortPolicy
通过抛出异常来拒绝处理任务

ThreadPoolExecutor.CallRunsPolicy
拒绝任务,直接让任务由调用线程执行,如果调用线程执行不了则直接丢弃

ThreadPoolExecutor.DiscardOledsPolicy
放缓一个最先安排的任务,先处理最后安排的任务

ThreadPoolExecutor.DiscardPolicy
放缓一个最后安排的任务,先处理最先安排的任务

tip:

实际开发中,线程池的线程数目怎么确定,设定成几合适

这里不能给出一个具体的个数,因为主机cpu的配置不确定,程序的执行特点(也就是代码里面具体干了啥)不确定,不确定代码是cpu密集型(做大量的算术运算,逻辑及判断)还是io密集型(做了大量的读写网卡,读写硬盘)

因此需要我们实际去针对程序进行性能测试,给线程池设置成不同的数目,记录每种情况下程序的核心指标和系统负载情况,最终选择一个合适的配置。

常见的锁策略:

乐观锁和悲观锁 

乐观锁认为每次拿数据认为这个数据是未经修改的,因此不会加锁,可以用CAS算法来实现

悲观锁认为每次拿取数据这个数据都是被修改之后的,因此次次加锁,别人想取这个数据就会产生阻塞。

乐观锁干的事情少,悲观锁干的事情多(多在过程上看)

读写锁和普通互斥锁

普通的互斥锁,和sychronized就毫无区别,两个线程竞争一把锁的时候就会产生等待

读写锁分为加读锁和加写锁,

读锁和读锁不会有竞争,

读锁和写锁 以及 写锁和写锁之间有竞争

因为读的场景往往很多,写的场景往往恒少,因此读写锁相对于普通的互斥锁就少了很多锁竞争,优化了效率

重量级锁和轻量锁

多在结果上看,最终加锁解锁消耗的时间是多还是少

重量级锁就是加锁开销比较大 例如进入内核的加锁逻辑

轻量级锁就是加锁开销比较小 例如纯用户态的加锁逻辑

自旋锁和挂起等待锁

自旋锁是轻量级锁的一种典型实现

挂起等待锁是重量级锁的一种典型实现

自旋锁需要消耗大量的cpu反复询问当前锁是否就绪,但是获取锁很及时,所以消耗时间相对短

挂起等待锁获取锁没有那么及时,因此消耗时间更长

公平锁和非公平锁

公平锁是遵循先来后到,考虑锁竞争顺序,有序的竞争一个锁

非公平锁是不遵循先来后到,不考虑锁竞争顺序,全都可以站在同一起跑线取竞争一个锁

操作系统默认的锁的调度就是非公平的情况

想要实现公平锁,需要引入额外的数据结构来记录线程加锁的顺序,需要一定的额外开销

可重入锁和不可重入锁

可重入锁就是针对一个线程连续加锁两次不会死锁

不可重入锁就是针对一个线程连续加锁两次会死锁

synchronized

是乐观锁,也是悲观锁,是轻量级锁,也是重量级锁,乐观锁的部分是基于自旋锁实现的,悲观锁的部分是基于挂起等待锁实现的,是自适应的,初始使用是乐观锁,轻量级锁,自旋锁。

不是读写锁,是普通互斥锁,是非公平锁,是可重入锁

还是偏向锁,必要的时候就加锁,能不加就不加,偏向锁只是设置了一个状态

如果锁竞争激烈了,synchronized就会自动适应,切换成不同的锁

自适应是JVM在实现synchronized的时候给我们提供的自动优化的策略

synchronized效果就是加锁,两个线程针对同一个对象加锁的时候就会出现锁竞争,后来尝试加锁的线程就会阻塞等待,直到前一个线程释放锁

synchronized还会锁消除,由JVM自动判定,发现这个地方代码不需要加锁,就会自动把锁给去掉。比如有多个线程,但是多个线程不涉及修改一个变量,如果代码也写了synchronized这个时候JVM就会直接干掉这个加锁操作

锁消除也是一种编译器优化的行为

CAS

就是compare and swap

比较并交换,如果内存中某个值和cpu寄存器A中的值进行比较,如果两个值相同就把 另一个寄存器B中的值和内存值进行交换,(把内存的值放到寄存器B中,同时把寄存器B的值写给内存)如果不相同那么无事发生(不关心交换之后寄存器B的值,更关心交换之后内存的值)

上述是CPU的一条指令,原子完成的,线程是安全的

CAS只能在特定的场景用,加锁面更广

最常用的两个场景是,实现原子类和实现自旋锁

实现原子类就例如,count++在多线程环境下不安全,要安全就需要牺牲性能取加锁,我们就可以基于CAS操作来实现原子的,从而来保证线程的安全和高效

实现自旋锁就是纯用户态的轻量级锁

当发现锁被其他线程持有的时候,另外的线程不会挂起等待,而是会反复询问,看当前锁是否被释放。

CAS和ABA问题

在CAS中,进行比较的时候,会发现寄存器A和内存M的值相同

我们无法判断M是变了又变回来了还是没变

要怎么解决呢,我们只需要一个记录器来记录上述内存的变化就可以了

一是记录M的修改次数或者上次的修改时间

这个时候修改操作就比较版本号或者上次修改时间,如果发现版本号不一致就放弃操作

锁粗化

synchronized对应代码块包含的代码少,粒度就细,包含的代码多,粒度就粗

锁粗话就是把细粒度的加锁变成粗力度的加锁

在三个项目的开头都有加锁和解锁的情况下,只在开头去加锁和在末尾去解锁就是锁粗化,需要是同一个加锁对象才可以粗化到一起

 JUC

就是java.util.concurrent

主要介绍这个包内以下方法

Callable接口
类似于Runnable,Runnable描述的任务不带返回值,Callable描述的任务带返回值
package threading;

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 15:01
 */
public class L11111 {
//    创建线程,通过线程来计算1+2+3+...+1000
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //使用一个Callable来定义一个任务
        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不能直接传callable 需要中间类futuretask
        Thread t = new Thread(futureTask);
        t.start();

        //获取线程计算结果
        //get方法会阻塞,直到call方法计算完毕,get才会返回。
        System.out.println(futureTask.get());

    }
}

 

ReentrantLock

主要是三个方法:
lock()

unlock()

tryLock    
试试看能不能加上锁,试成功了就加锁成功,失败了就放弃,还可以指定加锁的等待超时时间

ReentrantLock
可以实现公平锁,默认是非公平的,加入一个简单参数就可以成为公平锁

Condition类
可以指定唤醒那个线程,等待哪个线程
package threading;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 16:17
 */
public class L1113 {
    public static void main(String[] args) {
        ReentrantLock locker = new ReentrantLock(true);
        //里面的true可以实现公平锁
        //加锁
        locker.lock();

        //这两者之间如果有return或者异常就有可能导致unlock执行不到
        //大部分锁都是上面这样设定的
        //但是synchronized就没有这个问题
        //解锁
//        locker.unlock();
//
//        try{
//            locker.lock();
//        }finally {
//            {
//                locker.unlock();
//            }
//        }
    //这样写更加稳妥


    }
}

原子类

底层是基于CAS实现的,java内部

AtomicBoolean
AtomicInteger
AtomicIntegerArray
AtomicLong
AtomicReference
AtomicStampedReference
package threading;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 16:29
 */
public class L1114 {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger count = new AtomicInteger(0);
//                //相对于count++
//                count.incrementAndGet();
//                //相对于++count
//                count.getAndDecrement();
//                //相对于count--
//                count.decrementAndGet();
//                //相对于--count
        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

信号量

p操作 申请一个资源 可用资源数-1

v操作 释放一个资源 可用资源数+1

当计数为0的时候,继续p操作就会阻塞等到其他线程v操作了为止

锁可以说是一个特殊的信号量,信号量是一个更为广义的锁。

package threading;

import java.util.concurrent.Semaphore;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 16:40
 */
public class L1115 {
    public static void main(String[] args) throws InterruptedException {
        //构造的时候需要指定初始值,计数器的初始值,表示有几个可用资源
        //这个信号量就是把操作系统中的信号量给封装了一下
        Semaphore semaphore = new Semaphore(4);
        //p操作 申请资源 计数器+1
        semaphore.acquire();
        System.out.println("p操作");
        semaphore.acquire();
        System.out.println("p操作");
        semaphore.acquire();
        System.out.println("p操作");
        semaphore.acquire();
        System.out.println("p操作");
        //v操作,释放资源 计数器-1
        semaphore.release();

    }
}

CountDownLatch

用于解析多个线程完成任务

package threading;

import java.util.concurrent.CountDownLatch;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2023-01-11
 * Time: 16:48
 */
public class L1116 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i <10 ; i++) {
            Thread t = new Thread(()->{
                System.out.println("开始执行任务 " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("结束执行任务 "+ Thread.currentThread().getName());
                //撞线了
                countDownLatch.countDown();
            });
            t.start();
        }
        //所有选手撞线后才会解除这个阻塞
        countDownLatch.await();
        System.out.println("比赛结束");
    }
}

多线程使用队列和哈希表

多线程使用队列
ArrayBlockingQueue
基于数组实现的阻塞队列

LinkedBlockingQueue
基于链表实现的阻塞队列

PriorityBlockingQueue
基于堆实现的带优先级的阻塞队列

TransferQueue
最多只包含一个元素的阻塞队列

多线程使用哈希表
Hashtable

ConcurrentHashMap

ConcurrentHashMap


背后做了很多优化给加锁操作:

1 锁粒度的控制
HashTable是直接在方法上加synchronized 相当于是对this加锁 
也就是相当于是针对哈希表对象来加锁
一个哈希表只有一个锁
多个线程,无论这些线程都是如何操作这个哈希表都会产生锁冲突了

而ConcurrentHashMap不是一把锁,而是多把,给每个哈希桶都分配一把锁
只有两个线程访问同一个哈希桶的时候才会有锁冲突,如果不是一个哈希桶就不会产生锁冲突
又因为哈希桶个数很多 因此大大降低了哈希桶的锁冲突概率

2 只是给写操作加锁,读操作就不加锁了。

因此两个线程同时修改,才会有锁冲突,两个线程同时读或者一个线程读一个线程修改的话是不会有冲突的。也不需要担心会读到正在修改的数据。读操作中广泛的使用了volatile来保证读到的数据是及时的

3 充分的利用到了CAS的特性

比如维护元素个数,都是通过CAS实现而不是加锁 还有些地方直接使用了CAS实现的轻量级锁来实现。整体思路是能不加就不加。核心思路是尽一切可能去降低锁冲突的概率

4 对于扩容操作,进行了特殊的优化

HashTable的扩容是这样的

当put元素的时候,发现自己的负载因子已经超过了阈值,就需要触发扩容(申请更大的数组,把旧的数据搬运到新的数组上)。主要问题是如果元素个数很多,这个操作的开销就会很大。

而ConcurrentHashMap就是每次搬运一点,旧的和新的数组会同时存在,每次进行哈希表的操作就会把旧的内存上的元素搬运一部分到新的空间桑,直到最终搬运完成,然后再释放旧的空间。如果是查询元素则旧的和新的一起查,如果是插入元素则直接往新的上插入,如果是删除元素则直接删了不用搬运了

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

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

相关文章

CAN总线控制器MCP2515 替代芯片 DP2515 DP2515-I/ST

汽车K总线与CAN的区别是什么 1、功能不同   K线一般用于检测系统&#xff0c;属单线模式&#xff0c;与诊断仪器连接并相互传递数据。CAN线主要用于控制单元与控制单元之间传递数据、属双线模式&#xff0c;分高位线和地位线。   2、通讯速度不同   K线通讯速率较低&…

101.对称二叉树 | 递归 + 迭代

对称二叉树 leetcode : https://leetcode.cn/problems/symmetric-tree/ 参考 对称二叉树 递归思路 首先在开始时, 一定要注意, 对称二叉树对比的并不是一个节点的左右子树, 而是两棵树, 这个很关键! 对比时是内侧和内侧对比, 外侧和外侧对比, 递归三步 : 确定递归的参数以…

1.1.2 了解JAVA语言

文章目录1 JAVA语言发展史2 面向对象的概念3 跨平台性4 JDK1 JAVA语言发展史 JAVA是由詹姆斯•高斯林&#xff08;James Gosling&#xff09;所创建的&#xff0c;其1977年获得了加拿大卡尔加里大学计算机科学学士学位&#xff0c;1983年 获得了美国卡内基梅隆大学计算机科学博…

4)Mybatis数据源以及事务实现

1. Mybatis数据源分为两种&#xff0c;一种直接连接数据库&#xff0c;一种使用连接池连接数据库&#xff0c;具体代码实现在包目录下 org.apache.ibatis.datasource 数据源接口&#xff1a; javax.sql.DataSource 池化数据源&#xff1a; org.apache.ibatis.datasource.…

OpenGL集锦(1)-安装与概述

目录概述&#xff46;&#xff45;&#xff44;&#xff4f;&#xff52;&#xff41;下安装编写OpenGL应用程序测试hello,world概述 OpenGL&#xff08;英语&#xff1a;Open Graphics Library&#xff0c;译名&#xff1a;开放图形库或者“开放式图形库”&#xff09;是用于…

Lichee_RV学习系列--CoreMark-Pro移植

Lichee_RV学习系列文章目录 Lichee_RV学习系列—认识Lichee Rv Dock、环境搭建和编译第一个程序 Lichee_RV学习系列—移植dhrystone 文章目录Lichee_RV学习系列文章目录一、CoreMark-Pro简介二、获取源码三、编译coremark-pro1、配置coremark-pro2、编译coremark-pro四、开发板…

各种树的总结

1.B树和B树 数据库的大量数据用什么存储&#xff1f;为什么是B树和B树&#xff1f;使用二叉树不行吗&#xff1f;先来说说他们的演变吧&#xff0c;首先如果用二叉树的话都为排好序的树查询起来是不是效率不高&#xff1f;所以此时我们提出了对树排序&#xff0c;就变成了二叉…

联想拯救者屏幕亮度无法调节,监视器和显卡驱动问题,经过多种测试

主要的问题位置 1&#xff0c;设备管理器中的监视器部分 2&#xff0c;设备管理器的显卡适配器部分 个人电脑出现这种情况的原因 自己拆一下机器加装固态&#xff0c;但这种感觉不应该导致问题。但导致这种问题的原因可能是装固态时候把电池拔了。 一些网上常说的方法 更新…

数字化转型对企业有什么意义?有哪些案例可以分享?

如何看待制造企业数字化转型&#xff1f;制造业企业数字化转型有哪些思路和案例&#xff1f; 一提到制造企业数字化转型&#xff0c;大多数人都认为&#xff0c;这是专属于大型制造企业的行为。其实不然&#xff0c;对于中小型制造企业&#xff0c;数字化转型也应该从易到难&a…

interview

1.PyTorch1.1 Conv2d1.2 dataset&#xff0c;dataloader1.3 训练pipeline1.4 梯度归零1.5 torch保存模型种类及区别2.目标检测2.1 yolo3,4,5,7区别2.2 yolo使用的loss(ciou,BCE等等)ciouBCElossL1,L2,CE,BCE2.3 图像增强2.4 IOU计算公式3.深度学习基础3.1 卷积公式4.TensorRT5.…

Niantic:未来AR重要场景,VPS众包3D地图到底是啥?

几个世纪以来&#xff0c;人们使用指南针、地图、星盘和象限仪来找路&#xff0c;而在过去二十年里&#xff0c;GPS成为了主流的定位系统&#xff0c;并且与手机结合后&#xff0c;让人们的出行越来越方便。而随着摄像头等技术发展&#xff0c;我们也开始看到视觉定位技术的崛起…

(almalinux,rockylinux,openeuler,openanolis,centos,ubuntu)云原生容器镜像漏洞trivy扫描对比

一、下载并安装trivy漏洞扫描工具 下载&#xff1a; https://github.com/aquasecurity/trivy/releases/download/v0.31.3/trivy_0.31.3_Linux-64bit.rpm 以下为centos平台的安装&#xff1a; [rootlocalhost ~]# rpm -ivh trivy_0.31.3_Linux-64bit.rpm Preparing... …

【算法刷题 DAY03】剑指offer树相关算法题总结2

JZ7 重建二叉树 描述 给定节点数为 n 的二叉树的前序遍历和中序遍历结果&#xff0c;请重建出该二叉树并返回它的头结点。 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}&#xff0c;则重建出如下图所示。 提示: 1.vin.length pre.length 2.pre 和…

CSS入门二、美化页面元素

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

【高速数字化仪应用案例系列】虹科数字化仪在光纤领域的应用

光纤应用 光纤越来越多地应用于各种领域。它们能够以光速长距离传输信息&#xff0c;并且损耗低&#xff0c;这使它们成为大容量远程数据通信的主要媒介。因此&#xff0c;光纤网络可以在电信系统中找到&#xff0c;它们用于传输和接收的目的。它们还用于提供各种数字服务&…

Docker命令-常用命令讲解

Docker常用命令 一&#xff1a;帮助命令二&#xff1a;镜像命令1. docker images 查看所有本地的主机上的镜像2. docker search 镜像名3. docker pull 下载镜像4. docker rmi三&#xff1a;容器命令1.docker run 新建容器并启动2.从容器返回到主机&#xff1a;3.docker ps 列出…

收银软件哪家强?2023年收银软件排行榜新鲜出炉!

每家实体店都少不了收银的程序&#xff0c;每个实体店老板都离不开收银的工具。随着信息技术的发展&#xff0c;收银的工具不再只有收银机&#xff0c;更高效、更方便的收银软件&#xff0c;已经成为了零售店老板们的新宠。收银机和收银软件有什么区别&#xff1f;收银机只能对…

1.11 LED灯点亮串口解析器

LED点灯实验 一&#xff0e;电路图&#xff1a; 三极管&#xff1a; NPN类型&#xff1a; PNP类型&#xff1a; NPN类型当基极为高电平时&#xff0c;集电极和发射极导通 PNP类型当基极为低电平时&#xff0c;集电极和发射极导通 由电路图可知LED电路图中三极管为NPN类型&am…

我在CSDN的2022---2023Flag

一、加入CSDN我是在2020年12月注册的CSDN&#xff0c;大一上学期就听同学给我讲了这个软件&#xff0c;然后就下载了&#xff0c;里面确实很多优质文章&#xff0c;对于当时向我们这样的初学者来说就是很实用。还记得都是搜什么&#xff0c;求最大值&#xff0c;最小值&#xf…

Redis热点数据处理

1、概念热点数据就是访问量特别大的数据。2、热点数据引起的问题流量集中&#xff0c;达到物理网卡上限。请求过多&#xff0c;缓存分片服务被打垮。redis作为一个单线程的结构&#xff0c;所有的请求到来后都会去排队&#xff0c;当请求量远大于自身处理能力时&#xff0c;后面…