面试八股-Java并发

news2025/1/20 5:49:38

1.线程

和进程区别:

  • 进程:独立地址空间 代码、数据、堆栈 资源分配基本单位
  • 线程:共享地址空间 线程ID、指令指针、寄存器集合和堆栈 调度分派基本单位

1.1.使用

  • 实现Runable接口,run方法为实际逻辑
  • 实现Callable接口,可以有返回值,通过FutureTask进行封装再进入Thread参数
  • 继承Thread类
    • 不能继承其他类
    • 开销过大

1.2.状态

特指Java虚拟机的状态,而不是操作系统中的

  • NEW 创建未启动,其他状态start会产生IllegalThreadStateException异常。
  • RUNNABLE 正在Java虚拟机中运行
  • BLOCKED 请求获取monitor lock从而进入synchronized函数或者代码块,但是后者已经被占用
  • WAITING 等待线程显式唤醒,阻塞是被动的,等待是主动的,通过调用Object.wait等方法进入。
    • 未设置Timeout参数的wait方法
    • 未设置参数的join方法
    • LockSupport.part方法
  • TIMED_WAITING 等待一段时间后自动唤醒
    • Thread.sleep
    • 设置时间参数的join方法
    • 设置时间参数的wait方法
    • LockSupport.parkNanos方法
    • LockSupport.parkUntil方法
  • TERMINATED 死亡,线程自己结束或者异常结束

1.3.常用方法

1.3.1.静态

  • interrupted 返回当前线程是否被中断
  • sleep 休眠当前线程,单位为毫秒
  • yield 作为给线程调度器建议,告诉可以切换给其他线程执行了
  • currentThread 当前正在执行的线程

1.3.2.实例

  • setDaemon 设置后台服务线程,负责给其他非守护线程提供服务,所有非守护进程结束时守护线程消亡,如垃圾收集线程。守护线程在程序结束时中断不应该产生不良后果,如数据库录入等不能设置为守护线程。
  • getName
  • getId
  • interrupt()
  • isAlive
  • getPriority 获取优先级

1.4.线程中断相关内容

  • InterruptedException 调用线程的interrupt来中断线程,如果线程阻塞,就会抛出该异常提前结束线程,无法阻断IO阻塞和synchronized阻塞。
  • interrupted 读取interrupt方法设置的中断标记,如果没有sleep的无限循环无法用interrupt方法抛出异常使其终止。
  • isInterrupted 判断是否发生了中断。
  • Executor中断操作,shutdown方法等待全部方法执行完毕。shutdownNow,相当于调用每个进程的interrupt方法再关闭。
  • Executor的submit方法会返回Future对象,可以调用cancel方法停止他。

1.5.线程协作/通信

按次序完成某个任务,如下的命令都是在多个线程的情况下调用才有意义。

  • join B线程中调用A线程的join,需要等待A线程结束之后再继续B的语句,B会放到阻塞队列。

  • Object的方法

    • wait 使线程挂起,等待其他线程用notify由JVM指定和notifyAll将其唤醒,后者让其竞争锁
    • 只能在同步方法或者同步控制块中使用
    • 使用wait挂起期间,线程会释放锁,如果没有释放,那么其他对象就没法进入对象同步方法或者同步控制块
    • wait是Object的方法,会释放锁,sleep是Thread的方法,不会释放锁
    • 每个锁对象有两个队列,就绪队列存放将要竞争锁的线程,阻塞队列存储了阻塞的线程
  • Condition的方法

    • 使用Lock.newCondition来获得一个Condition对象
    • await使线程等待
    • singal或者singalAll唤醒等待线程,比wait更加灵活
  • BlockingQueue 使用阻塞队列进行通信

  • sleep和wait的区别

waitsleep
同步只能在同步上下文中使用任何地方都可以调用
作用对象定义在Object中,作用于对象定义在Thread中,作用于当前线程
释放锁资源
唤醒条件其他线程使用notify或者notifyAll超时或者调用interrupt方法
方法属性实例方法静态方法

1.6.线程安全

多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为。

1.6.1.不可变

  • final 关键字修饰的基本数据类型
  • String
  • 枚举类型
  • Number 部分子类,如 Long 和 Double 等数值包装类型,BigInteger 和 BigDecimal 等大数据类型。但同为 Number 的原子类 AtomicInteger 和 AtomicLong 则是可变的。

对于集合类型,可以使用 Collections.unmodifiableXXX() 方法来获取一个不可变的集合。其对原始集合进行拷贝,需要对其修改的方法都会抛出异常。

1.6.2.互斥同步

synchronized

  • synchronized
    • 用在代码块时作用于同一个对象,两个对象上的同步代码块不会进行同步。由monitorenter和monitorexit指令实现,每个对象都是一个监视器锁monitor,被占用就会处于锁定状态。
    • 用在方法时也是作用于同一个对象。通过ACC_SYNCHRONIZED标示符号实现,设置的话先尝试获得monitor,获得才能执行。
    • 用在类上时(synchronized(类名.class)),调用同一个类的不同对象也会进行同步。
    • 同步静态方法,也会作用于同一个类。
    • 不能同步静态代码块!!
    • 不可中断,避免发生死锁

ReentrantLock

  • ReentrantLock
    • Lock lock = new ReentrantLock,传入参数决定其是否为公平锁
    • lock.lock加锁,lock.unlock解锁
    • 记得在finally中释放锁
    • lock 如果获取锁立刻返回,否则休眠等待锁
    • tryLock 获取到返回true,否则false
    • lockInterruptibly 获取到锁立刻返回,没有获取则锁定,处于休眠状态,直到锁定
    • 通过AQS实现,内部实现了两个队列的抽象类,同步队列和条件队列。其中同步队列是一个双向链表,里面储存的是处于等待状态的线程,正在排队等待唤醒去获取锁,而条件队列是一个单向链表,里面储存的也是处于等待状态的线程,只不过这些线程唤醒的结果是加入到了同步队列的队尾,AQS所做的就是管理这两个队列里面线程之间的等待状态-唤醒的工作。
  • ReentrantReadWriteLock
    • 读之间不互斥,读写和写写互斥,传入参数决定是否公平锁
    • 持有写锁可以获取读锁,持有读锁获取写锁就会死锁
  • StampedLock
    • 首先检测版本号是否一致,一致说明没有修改,否则说明修改了,使用悲观锁获取数据
    • 代码更加复杂,StampedLock是不可重入锁
    • 提供更复杂的将悲观读锁升级为写锁的功能,用在if-then-update场景

使用AQS实现

使用FairSync和NonfairSync来实现公平锁和非公平锁。

img

ReentrantLock和synchronized比较和使用

synchronizedReentrantLock
实现JVMJDK
性能新版本进行自选锁等优化,性能大致和Reentrantlock相同差不多
等待可中断(线程放弃等待)不可中断可中断
公平锁(按申请时间顺序依次获取)非公平默认非公平,也可公平
绑定多个条件可以绑定多个Condition对象
出现异常是否释放锁
是否可知获得锁成功
使用选择JVM原生支持,会保证锁的释放,优先选择* 公平锁
* 灵活使用
* interrupt中断
* 实现线程精确唤醒

1.6.3.非阻塞同步

  • CAS 乐观锁,靠硬件完成,比较并交换,相等则替换。
  • AtomicInteger J.U.C包里面的整数原子类调用了Unsafe类的CAS操作。IncrementAndGet->getAndAddInt->getIntVolatile(获取相对地址偏移)->compareAndSwapInt。
  • ABA J.U.C提供了AtomicStampedReference来解决ABA问题,如果要解决,采用传统互斥同步会比原子类更高效。

1.6.4.无同步方案

如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性。

  • 栈封闭 多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。

  • 线程本地存储(Thread Local Storage)

    • 尽量将共享数据的代码放在同一个线程中
    • 很多应用符合这个特点,如Web线程
    • 使用ThreadLocal类实现本地存储,底层由ThreadLocalMap实现,每个线程一个ThreadLocalMap
    • ThreadLocalMap并不是Hashmap,而是类似于数组的解构,使用线性开址寻址法定位插入位置
    • Entry为WeakReference,因为如果为强引用即时设为null也不会回收
    • 实际的ThreadLocalMap存放在堆上,栈上只保存引用
  • 由于Map底层数据结构可能存在内存泄露的情况,尽可能每次使用后手动调用remove

  • 可重入代码

    • 纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。
    • 不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。

1.7.Synchronized中的锁优化

优化主要是JVM实现的。

锁的四种状态:

  • 无锁状态(unlocked)
  • 偏向锁状态(biasble):让第一个获得锁的线程之后获取锁不再进行同步操作,甚至CAS也不需要,直到另一个线程尝试获取锁对象返回无锁状态或者进入轻量级锁。
  • 轻量级锁(lightweight locked):使用自旋锁,CAS操作来避免重量级锁使用互斥量的开销,CAS失败时首先检查对象Mark Word是否指向当前虚拟机栈,是的话说明已经有了锁,否则说明已经被抢占。两条以上线程争用同一个锁,膨胀为重量级。十次升级。
  • 重量级锁(inflated)

其他措施:

  • 自旋锁:适用于锁定状态只持续一段时间的情况,在执行忙循环中获得锁避免进行阻塞,JDK中引入自适应自旋锁,由前一次同一个锁上自旋次数及锁拥有者来决定。
  • 锁清除:对于被检测出不可能存在竞争的共享数据的锁进行消除。通过逃逸分析来支持,如果堆上的共享数据不可能被其他线程访问到,就可以消除锁。
  • 锁粗化:避免对同一个对象反复加锁造成性能损耗。如果反复对同一个对象加锁,就将范围粗化到整个操作序列外部。

对象头的mark word存放什么

  • 对象的hashcode
  • gc信息
  • 锁信息
  • 指向对象的类的指针

1.8.良好实践

  • 线程给予有意义的名字
  • 缩小同步范围,同步块而不是方法
  • 多用同步工具少用wait和notify,同步类简化编码操作,避免复杂控制流
  • 使用Blocking实现生产者消费者问题
  • 多用并发集合少用同步集合,ConcurrentHashMap而不是Hashtable
  • 使用本地变量和不可变类保证线程安全
  • 使用线程池而不是直接创建线程

1.9.锁池和等待池

  • 锁池:所有竞争同步锁的线程都会放入锁池时
  • 等待池:执行wait方法之后进入等待池,等待notify或者notifyall之后才竞争锁

2.内存模型

2.1.volatile

添加内存屏障,lock指令

  1. 保证可见性 写volatile变量时强制把本地线程变量刷新到主内存去,让其他线程volatile变量缓存无效。
  2. 禁止指令重排 前面语句都执行完,后面都未执行,前面结果对后面可见

2.2.主内存和工作内存

处理器上的寄存器的读写速度比内存快好几个数量级,为了解决这种速度矛盾,在他们之间增加了高速缓存,并使用一致性协议解决缓存之间数据不一致问题。

所有变量都存储在主内存中,每个线程还有自己的工作内存,工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的主内存的副本拷贝。线程只能操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成(Load和Store)。

2.3.内存间互操作

  • Thread到工作内存
    • use:把工作内存中一个变量的值传递给执行引擎
    • assign:把一个从执行引擎接收到的值赋给工作内存的变量
  • 工作内存到主内存
    • read:把一个变量的值从主内存传输到工作内存中
    • load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
    • store:把工作内存的一个变量的值传送到主内存中
    • write:在 store 之后执行,把 store 得到的值放入主内存的变量中
  • 主内存内部
    • lock
    • unlock

2.4.内存模型三大特性

  • 原子性 Java内存模型保证了操作的原子性,对int操作是原子的,Long和Double没有被volatile修饰,读写可能划分为两次进行。多线程环境下int之间可能还会并发,用原子类或者synchronized方法修饰。
  • 可见性 一个线程修改了共享变量的值,其他线程能够立即得知这个修改,Java内存模型通过变量修改之后将新值同步主内存,变量读取之前从主内存刷新变量实现可见性。
  • 有序性 线程内观察,所有操作都是有序的,一个线程观察另一个线程,所有操作都是无序的,因为发生了指令重排。
    • volatile 对变量 添加内存屏障,防止指令重排,不阻塞,原子,不优化
    • synchronized 对对象和类 保证有序性,一个时刻只有一个线程同步代码,原子,有序,可见,编译器优化

2.5.先行发生原则

不需要控制就能让一个操作先于另一个操作完成。

  • 单一线程原则 线程内前面操作先于后面
  • 管程锁定操作 一个unlock先行与后面的锁的lock操作
  • volatile变量规则 对于一个volatile的写操作先于其读操作
  • 线程启动规则 Thread对象的start方法先于此线程每一个动作
  • 线程加入规则 Thread对象结束先于join方法返回
  • 线程中断规则 对线程interrupt方法的调用先于中断被检测
  • 对象终结原则 对象初始化完成先于finalize方法开始
  • 传递性 A先于B B先于C,则A先于C

3.原子操作类

3.1.LongAdder

JDK8新增的用于并发环境都计数器,高并发情况下,代替AtomicLong,因为其用锁分段实现,并发计数时不同线程可以在不同计数单元进行计数,最后和base相加得到最终结果,减少线程竞争,提高并发效率,但是可能会造成统计数据有所误差。高并发优先使用LongAddr,但是如果需要准确数值,如序号生成,全局唯一的AtomicLong才是正确选择。

3.2.Unsafe

  • 分配内存,释放内存,allocateMemory,reallocateMemory,freeMemory等
  • 挂起和唤醒操作,LockSupport类中使用
  • CAS操作

3.3.原子更新基本类型

保证多线程下不会出现超卖等情况。

  • AtomicInteger

    • 调用Unsafe的getAndAddInt实现incrementAndGet
    • volatile和native保证原子性,避免synchronized高开销
  • AtomicBoolean

    • 核心是compareAndSet方法,转变10变量然后针对int变量更新方法
  • AtomicLong

    • 实现原理和AtomicInteger一致,但是针对long变量

3.4.原子更新数组

使用方式和基本类型基本一致,只是会多个索引位,适用于管理一批原子变量。

  • AtomicIntegerArray 整型数组的某个元素

  • AtomicLongArray 长整型数组的某个元素

  • AtomicReferenceArray

构造传入数组。

3.5.原子更新引用类型

需要原子引用类型变量的话使用。

  • AtomicReference
  • AtomicReferenceFieldUpdater。原子更新引用类型里的字段。
  • AtomicMarkableReference。原子更新带有标记位的引用类型。

原子更新引用类型

原子更新引用类型里的字段

原子更新带有标记位的引用类型。

3.6.原子更新字段类

  • AtomicIntegeFieldUpdater 原子更新整型字段
  • AtomicLongFieldUpdater 原子更新长整型字段
  • AtomicStampedReference。原子更新引用类型,这种更新方式会带有版本号,为了解决 CAS 的 ABA 问题。

使用需要两步操作

  • 原子更新字段类都是抽象类,只能通过类静态方法 newUpdater 来创建一个更新器,并且需要设置想要更新的类和属性
  • 更新类的属性必须使用 public volatile 进行修饰

4.线程池

其复用原理是使得线程和任务解耦,不再是一对一关系,让每个线程执行一个循环任务,不断判断是否有任务需要被执行,有就直接run。

4.1.优势

  • 复用线程,降低线程的创建销毁开销
  • 提高线程响应速度,任务不需要等线程创建
  • 提高线程可管理性

4.2.常用类

  • Executor:一个运行新任务的简单接口;
  • ExecutorService:扩展了Executor接口。添加了Future 功能和一些用来管理执行器生命周期和任务生命周期的方法;
  • Executors:作为工厂创建若干常用线程池
    • Executors.newSingleThreadPool() 单线程
    • Executors.newFixedThreadPool() 定长线程池
    • Executors.newCachedTheadPool() 可缓存线程池,实质是定参的ThreadPoolExecutor
    • Executors.newScheduledThreadPool() 支持定时和周期性任务执行
  • ScheduledExecutorService:扩展了ExecutorService。支持定期执行任务。提供submit、shutdown等方法接口

4.3.ThreadPoolExecutor

推荐使用该方式来创建线程池,可以控制核心线程数、最大线程数等信息。

4.3.1.参数

  • corePoolSize 核心线程数

    • CPU密集型配置少的线程数量cpu数量+1
    • IO密集型配置多线程,2xcpu数量
    • 混合型则查看是否能拆分,如果拆分后相差时间不大就可以拆分,太大就没必要拆分
    • Runtime.getRuntime().availableProcessors()方法获得当前设备的 CPU 个数。
  • maximumPoolSize 最大线程数

  • keepAliveTime 超过核心线程数后的线程存活时间

  • unit 时间单位

  • workQueue 使用到的阻塞队列

    • 优先级不同任务可以使用PriorityBlockingQueue,但要注意饿死现象
    • IO密集型最好使用有界队列,因为无界队列可能会导致积压任务占用过多资源
  • threadFactory 线程工厂,提供创建新线程的功能

  • handler 超过最大线程数的拒绝策略

    • ThreadPoolExecutor. AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
    • ThreadPoolExecutor. CallerRunsPolicy:调用执行自己的线程运行任务。
    • ThreadPoolExecutor. DiscardPolicy: 不处理新任务,直接丢弃掉。
    • ThreadPoolExecutor. DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
    • 此外还可以自定义策略模式

4.3.2.execute方法执行(执行流程)

提交不需要返回值的任务,无法判断任务是否被线程池执行与否。

  1. 如果当前线程数小于corePoolSize,创建新线程执行任务
  2. 线程数达到了corePoolSize,试图将任务添加到任务队列中,这里先添加队列是因为新创建线程时需要获取全局锁,耗费资源,影响效率,所以先让队列去堆积更好。
  3. 队列已满,添加新线程,直到达到maximumPoolSize,然后调用处理策略处理新任务。

4.3.3.submit方法执行

用于提交有返回值的任务,返回Future类型对象,通过该对象判断是否执行成功,用get返回值

4.3.4.关闭线程池

  • shutdownNow 将线程池状态设置为STOP,然后尝试停止所有正在执行和未执行任务的线程,并返回等待任务执行的列表
  • shutdown 将线程池状态设置为SHUTDOWN,不再接受新任务,中断所有空闲线程,等所有现存任务完成之后销毁线程池。
  • isShutdown 两个关闭命令都会使其返回true
  • isTerminated 只有第二个命令才会使其立即返回true

4.4.线程池的状态

  • RUNNING:可以接受新提交任务,能够处理阻塞队列中的任务
  • SHUTDOWN:关闭状态,不能接受新提交任务,可以处理阻塞队列中的任务 shutdown()
  • STOP:不接受新的任务,也不处理阻塞队列中的任务。shutdownNow()
  • TIDYING:所有任务都终止,workerCount为0,线程池进入该状态后调用terminated()方法进入TERMINATED状态
  • TERMINATED:最终状态,terminated()
    • 线程池不是RUNNING
    • 不是TIDYING状态或TERMINATED状态
    • 是SHUTDOWN且workerQueue为空

5.同步工具

5.1.Semaphore

控制线程是否能够进入某一同步代码区,相比synchronized,Semaphore可以指定多个进入,从测试来看,默认采用非公平模式(返回乱序)。

  • semaphore.acquire() P
  • semaphore.release() V

5.2.CountDownLatch

允许一个或者多个线程一直等待,直到其他线程执行完之后再执行。

  • 构造器传入线程个数
  • CountDownLatch.countDown() 减少一个等待
  • CountDownLatch.await() 等待其他线程执行完之后再执行,即等待数归零

5.3.CyclicBarrier

实现线程间的计数等待,可以循环使用,一轮集体结束执行下一轮。

  • 构造器第一个参数传入需要等待的线程数量,第二个参数传入全部结束之后执行的任务
  • CyclicBarrier::await() 增加一个结束线程
  • CyclicBarrier::reset() 计数器清零,可以复用
  • CyclicBarrier::getNumberWaiting获取当前线程数量
  • CyclicBarrier::isBroken 直到线程是否中断

6.阻塞队列

阻塞队列可以保留超出长度的任务,其原理是阻塞超出的线程,让其成为wait状态,释放CPU资源,由其自动阻塞、唤醒。

6.1.分类

  • ArrayBlockingQueue 一个由数组结构组成的有界队列
  • LinkedBlockingQueue 链表 有界队列
  • PriorityBlockingQueue 优先队列无界阻塞队列
  • DelayQueue 优先级队列实现的无界阻塞队列
  • SynchronousQueue 不存储元素的阻塞队列
  • LinkedTransferQueue 链表组成的无界队列
  • LinkedBlockingQueue: 链表 双向 阻塞队列

6.2.添加元素

  • add:成功返回true,满了失败抛出IllegalStateException
  • offer:返回true或者false代表成功或者失败
  • put:满了阻塞直到不满

6.3.删除元素

  • poll:删除头部元素,为空返回null,否则返回元素
  • remove:基于对象找到对应元素删除,返回true或者false
  • take:删除头部元素,如果为空阻塞直到有元素删

7.并发容器

  • ConcurrentHashMap: 线程安全版HashMap。
  • ConcurrentLinkedQueue: 线程安全版 LinkedList。
  • ConcurrentSkipListMap: 线程安全版跳表 Map。
  • CopyOnWriteArrayList: 线程安全版 List,但是不是通过锁实现。在读多写少的场合性能非常好。
  • LinkedBlockingQueue: 线程安全的阻塞队列。
  • PriorityBlockingQueue: 支持优先级的无界阻塞队列。

8.AQS

8.1.状态

使用int变量表示同步状态,内置FIFO队列来完成获取资源线程的排队工作。

  • ReentrantLock 用其表示锁的持有者线程已经重复获取该锁的次数,如果大于0,其他线程无法获取锁,进入同步等待队列。
  • Semaphore 表示剩余的许可数量,为0进入阻塞队列。
  • FutureTask 表示任务状态 未开始、运行等。
  • ReentrantReadWriteLock 前16位是读锁,后16位是写锁
  • CountDownLatch 使用state表示计数次数,state大于0,表示需要加入到同步等待队列并阻塞,等于0才逐一唤醒等待队列。

8.2.作用

  • 同步状态(state)的维护管理
  • 等待队列的维护管理
  • 线程的阻塞和唤醒

8.3.实现组件

  • 独占式:ReentrantLock
  • 共享式:Semaphore CountDownLatch
  • 组合式:ReentrantReadWriteLock

9.其他

9.1.DelayQueue

每个元素有过期时间,优先级队列,拿元素只拿过期元素。

9.2.Fork/Join框架

任务分割 执行任务合并结果

ForkJoinTask ,如果使用该框架,首先需要创建一个ForkJoin任务,该类提供了在任务中执行fork和join机制,一般继承子类:

  • RecursiveAction 没有返回结果的任务
  • RecursiveTask 返回结果的任务

ForkJoinPool ForkJoinTask需要通过ForkJoinPool来执行。

主要用于并行计算中,和 MapReduce 原理类似,都是把大的计算任务拆分成多个小任务并行计算。

ForkJoin 使用 ForkJoinPool 来启动,它是一个特殊的线程池,线程数量取决于 CPU 核数。

public class ForkJoinPool extends AbstractExecutorService

ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线程都维护了一个双端队列,用来存储需要执行的任务。工作窃取算法允许空闲的线程从其它线程的双端队列中窃取一个任务来执行。窃取的任务必须是最晚的任务,避免和队列所属线程发生竞争。例如下图中,Thread2 从 Thread1 的队列中拿出最晚的 Task1 任务,Thread1 会拿出 Task2 来执行,这样就避免发生竞争。但是如果队列中只有一个任务时还是会发生竞争。

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

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

相关文章

离散数学与组合数学-数理逻辑-02谓词演算及其形式系统

文章目录第二章 谓词演算及其形式系统2.1 个体谓词和量词2.1.1 个体谓词演算永真式谓词公式的前束范式一阶谓词演算形式系统谓词逻辑的等值演算与推理第二章 谓词演算及其形式系统 2.1 个体谓词和量词 2.1.1 个体 个体常元(constants):确定的个体用a,b,ca,b,ca,b,c等小写字母…

羟基聚乙二醇叠氮 HO-PEG-N3/Azide的结构式

羟基聚乙二醇叠氮(HO-PEG-N3)是异双功能PEG衍生物之一。叠氮化物在铜离子催化的水溶液中与炔基有效反应。炔烃和叠氮化物之间的1,3-偶极环加成反应是一种高产率的点击化学反应,可实现两个相应分子的高效结合。叠氮化物也可以与应变促进的环辛炔反应,不需…

Pr:使用作品

利用作品 Production,可将大型复杂工作流拆分为多个可管理的 Pr 项目,并可在作品内跨项目、跨平台(macOS 和 Windows)引用媒体,无论这些资源是在本地存储或是共享的网络存储,作品可让一切保持同步。作品&am…

真希望你也明白runtime.Map和sync.Map

Map 官方介绍 One of the most useful data structures in computer science is the hash table. Many hash table implementations exist with varying properties, but in general they offer fast lookups, adds, and deletes. Go provides a built-in map type that imple…

Grafana监控大屏配置参数介绍(二)

Grafana 系列文章,版本:OOS v9.3.1 Grafana 的介绍和安装Grafana监控大屏配置参数介绍(一)Grafana监控大屏配置参数介绍(二) 上一篇文章已经介绍了图表可视化配置部分的 Panel options、Tooltip、Legend 3类…

音视频行业大势如何,优势在哪?

电信行业的变革: 从1G语音、2G短信、3G图片语音、4G视频到5G未来可期的新时代,见证了音视频行业的磅礴发展。 技术更新慢且门槛高 技术更新慢,技术门槛高,大部分技术沿用至今,依然保持生命力,技术人员成型…

http协议和websocket协议

http协议 HTTP 即超文本传输协议,是一种获取网络资源 (例如图像、HTML 文档) 的应用层协议,它是互联网数据通信的基础,由请求和响应构成。通常,首先客户端会发送 HTTP 请求(在请求报文中会指定资源的 URL),然后用传输…

DocArray 0.20.0 发布!新增 Milvus 后端支持,更好地嵌套数据搜索,新增 RGB-D 格式的 3D 模型表示...

DocArray 是一个用于处理、传输和存储多模态数据的 Python 工具包。DocArray 提供便捷的多模态数据处理功能,具备基于 Protobuf 提供高性能的网络传输性能,同时也为多种向量存储方案提供统一的 API 接口。GitHub:github.com/docarray/docarra…

AU如何为你的人声增加空旷感?

你知道怎么使用AU给你的声音添加延迟效果,让你的声音具有空旷感和弱回声的效果。在这里我们可以使用插件达到这个目的。 在使用模拟延迟插件之前呢,我们可以去创建一个立体声总音轨,创建方式如图,跟着序号走,我们就可以…

CSS -- 06. CSS高阶技巧总结

文章目录CSS高阶技巧1 精灵图(sprites)1.1 为什么使用精灵图1.2 精灵图的使用2 字体图标2.1 字体图标的产生2.2 字体图标的优点2.3 字体图标的下载2.4 字体图标的引入2.5 字体图标的追加3 CSS三角形4 CSS用户界面样式4.1 鼠标样式 cursor4.2 表单的轮廓线4.3 防止拖拽文本域 re…

JAVA毕业设计——基于Springboot+vue的心理咨询管理系统(源代码+数据库)

github代码地址 https://github.com/ynwynw/psychlolgyhealth-public 毕业设计所有选题地址 https://github.com/ynwynw/allProject 基于Springbootvue的心理咨询管理系统(源代码数据库) 一、系统介绍 本项目分为管理员与普通用户两种角色 管理员角色包含以下功能&#xff…

[附源码]Python计算机毕业设计SSM基于Web美食网站设计(程序+LW)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

Kotlin标准库函数

Kotlin标准库中包含了几个函数,它们的目的就是可以在对象的上下文中执行代码块。当我们调用该Lambda表达式时,它会形成一个临时的作用域。在该范围内,可以访问不带名称的对象,此类函数称为作用域函数。包括: apply函数let函数run…

Java开发才不到3年,来面试开口要25K,面完连10K都不想给

前言 我的好朋友兼大学同学老左家庭经济情况不错,毕业之后没两年自己存了点钱加上家里的支持,自己在杭州开了一家网络公司。由于公司不是很大所以公司大部分的开发人员都是自己面试的,近期公司发展的不错,打算扩招也面试了不少人。…

[l论文解析]Classifier-Free Diffusion Guidance

paper link:https://openreview.net/pdf?idqw8AKxfYbI 文章目录OverviewWhat problem is addressed in the paper?What is the key to the solution?What is the main contribution?Potential fundamental flaws; how this work can be improved?Content关于 c…

Java 字符串 split 的一个反直觉陷阱

最近生产环境遇到一个奇怪的数组下标越界报错,如下图代码所示,我们可以肯定的是 fieldName 变量不为空(不是空字符串,也不是 null),但是代码执行到读取 names[0] 变量的时候,抛出了一个 数组下标…

5G无线技术基础自学系列 | 抗衰落技术

素材来源:《5G无线网络规划与优化》 一边学习一边整理内容,并与大家分享,侵权即删,谢谢支持! 附上汇总贴:5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 无线信道是随机时变信道,信…

【云计算与大数据技术】文件存储格式行式、列式、GFS、HDFS的讲解(图文解释 超详细)

一、分布式文件系统 文件系统最后都需要以一定的格式存储数据文件,常见的文件存储布局有行式存储、列式存储以及混合式存储三种,不同的类别各有其优缺点和适用的场景,在目前的大数据分析系统中,列式存储和混合式存储方案因其特殊…

mysql 数据库设计三大范式

1. 什么是设计范式 设计表的依据,按照范式设计出来的表,不会出现数据的冗余 数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构清晰的;反之则是乱七八糟,不仅会给开发人员制造麻烦&a…

大数据面试之Hive常见题目

大数据面试之Hive常见题目 1. Hive的架构 1、重点四个器:解释器(SQL Parser)、Driver:编译器(Compiler),优化器(Optimizer),执行器(Executor&…