当前位置: 首页 > news >正文

广东网站建设微信官网开发/业务多平台怎么样

广东网站建设微信官网开发,业务多平台怎么样,济南seo优化外包,上海外贸人才网Synchronized JVM系统锁一个对象里如果有多个synchronized方法,同一时刻,只要有一个线程去调用其中的一个synchronized方法,其他线程只能等待!锁的是当前对象,对象被锁定后,其他线程都不能访问当前对象的其…

Synchronized JVM系统锁

一个对象里如果有多个synchronized方法,同一时刻,只要有一个线程去调用其中的一个synchronized方法,其他线程只能等待!锁的是当前对象,对象被锁定后,其他线程都不能访问当前对象的其他synchronized方法。

非静态同步方法,用的是同一把锁!

当一个线程视图访问同步代码块时,首先必须获得锁,退出或者抛出异常时,释放锁!

特性:原子、可重入、有序、可见、不可中断

synchronized 实现原理

前言

synchronized 锁在 Java 中经常使用它的源码是 C++ 实现的,它的实现原理是怎样的呢?本文以 OpenJDK8 为例探究以下内容。

  • synchronized 是如何工作的

  • synchronized 锁升级过程

  • 重量级锁的队列之间协作过程和策略

对象头

对象头的内容非常多这里我们只做简单介绍以引出后文。在 JVM 中对象布局分为三块区域:

  • 对象头

  • 实例数据

  • 对齐填充

Mark Word:HashCode、GC信息、锁信息

Klass Pointer: 类型指针指向它元数据的指针

当线程访问同步块时首先需要获得锁并把相关信息存储在对象头中。所以 waitnotifynotifyAll 这些方法为什么被设计在 Object 中或许你已经找到答案了。

Hotspot 有两种对象头:

  • 数组类型,使用 arrayOopDesc 来描述对象头

  • 其它,使用 instanceOopDesc 来描述对象头

对象头由两部分组成

  • Mark Word:存储自身的运行时数据,例如 HashCode、GC 年龄、锁相关信息等内容。

  • Klass Pointer:类型指针指向它的类元数据的指针。

64 位虚拟机 Mark Word 是 64bit 其结构如下:

在 JDK 6 中虚拟机团队对锁进行了重要改进,优化了其性能引入了 偏向锁轻量级锁适应性自旋锁消除锁粗化等实现,其中 锁消除锁粗化本文不做详细讨论其余内容我们将对其进行逐一探究。

总体上来说锁状态升级流程如下:

无锁-->偏向锁-->轻量级-->重锁

偏向锁

流程

当线程访问同步块并获取锁时处理流程如下:

  1. 检查 mark word线程 id

  1. 如果为空则设置 CAS 替换当前线程 id。如果替换成功则获取锁成功,如果失败则撤销偏向锁。

  1. 如果不为空则检查 线程 id为是否为本线程。如果是则获取锁成功,如果失败则撤销偏向锁。

持有偏向锁的线程以后每次进入这个锁相关的同步块时,只需比对一下 mark word 的线程 id 是否为本线程,如果是则获取锁成功。

如果发生线程竞争发生 2、3 步失败的情况则需要撤销偏向锁。

偏向锁的撤销

  1. 偏向锁的撤销动作必须等待全局安全点

  1. 暂停拥有偏向锁的线程,判断对象是否处于被锁定状态

  1. 撤销偏向锁恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态

优点

只有一个线程执行同步块时进一步提高性能,适用于一个线程反复获得同一锁的情况。偏向锁可以提高带有同步但无竞争的程序性能。

缺点

如果存在竞争会带来额外的锁撤销操作。

轻量级锁

加锁

多个线程竞争偏向锁导致偏向锁升级为轻量级锁

  1. JVM 在当前线程的栈帧中创建 Lock Reocrd,并将对象头中的 Mark Word 复制到 Lock Reocrd 中。(Displaced Mark Word)

  1. 线程尝试使用 CAS 将对象头中的 Mark Word 替换为指向 Lock Reocrd 的指针。如果成功则获得锁,如果失败则先检查对象的 Mark Word 是否指向当前线程的栈帧如果是则说明已经获取锁,否则说明其它线程竞争锁则膨胀为重量级锁。

解锁

  1. 使用 CAS 操作将 Mark Word 还原

  1. 如果第 1 步执行成功则释放完成

  1. 如果第 1 步执行失败则膨胀为重量级锁。

优点

其性能提升的依据是对于绝大部分的锁在整个生命周期内都是不会存在竞争。在多线程交替执行同步块的情况下,可以避免重量级锁引起的性能消耗。

缺点

在有多线程竞争的情况下轻量级锁增加了额外开销。

自旋锁

自旋是一种获取锁的机制并不是一个锁状态。在膨胀为重量级锁的过程中或重入时会多次尝试自旋获取锁以避免线程唤醒的开销,但是它会占用 CPU 的时间因此如果同步代码块执行时间很短自旋等待的效果就很好,反之则浪费了 CPU 资源。默认情况下自旋次数是 10 次用户可以使用参数 -XX : PreBlockSpin 来更改。那么如何优化来避免此情况发生呢?我们来看适应性自旋。

适应性自旋锁


JDK 6 引入了自适应自旋锁,意味着自旋的次数不在固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果对于某个锁很少自旋成功那么以后有可能省略掉自旋过程以避免资源浪费。有了自适应自旋随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测就会越来越准确,虛拟机就会变得越来越“聪明”了。

优点

竞争的线程不会阻塞挂起,提高了程序响应速度。避免重量级锁引起的性能消耗。

缺点

如果线程始终无法获取锁,自旋消耗 CPU 最终会膨胀为重量级锁。

重量级锁

在重量级锁中没有竞争到锁的对象会 park 被挂起,退出同步块时 unpark 唤醒后续线程。唤醒操作涉及到操作系统调度会有额外的开销。

ObjectMonitor 中包含一个同步队列(由 _cxq_EntryList 组成)一个等待队列( _WaitSet )。

  • notifynotifyAll 唤醒时根据 policy 策略选择加入的队列(policy 默认为 0)

  • 退出同步块时根据 QMode 策略来唤醒下一个线程(QMode 默认为 0)

这里稍微提及一下管程这个概念。synchronized 关键字及 waitnotifynotifyAll 这三个方法都是管程的组成部分。可以说管程就是一把解决并发问题的万能钥匙。有两大核心问题管程都是能够解决的:

  • 互斥:即同一时刻只允许一个线程访问共享资源;

  • 同步:即线程之间如何通信、协作。

synchronizedmonitor锁机制和 JDK 并发包中的 AQS 是很相似的,只不过 AQS 中是一个同步队列多个等待队列。熟悉 AQS 的同学可以拿来做个对比。

队列协作流程图


源码分析

在 HotSpot 中 monitor 是由 ObjectMonitor 实现的。其源码是用 c++来实现的源文件是 ObjectMonitor.hpp 主要数据结构如下所示:

ObjectMonitor()  {_header       = NULL;_count        = 0;_waiters      = 0,       // 等待中的线程数_recursions   = 0;       // 线程重入次数_object       = NULL;    // 存储该 monitor 的对象_owner        = NULL;    // 指向拥有该 monitor 的线程_WaitSet      = NULL;    // 等待线程 双向循环链表_WaitSet 指向第一个节点_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;_cxq          = NULL ;   // 多线程竞争锁时的单向链表FreeNext      = NULL ;_EntryList    = NULL ;    // _owner 从该双向循环链表中唤醒线程,_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;_previous_owner_tid = 0; // 前一个拥有此监视器的线程 ID}
  1. _owner:初始时为 NULL。当有线程占有该 monitor 时 owner 标记为该线程的 ID。当线程释放 monitor 时 owner 恢复为 NULL。owner 是一个临界资源 JVM 是通过 CAS 操作来保证其线程安全的。

  1. _cxq:竞争队列所有请求锁的线程首先会被放在这个队列中(单向)。_cxq 是一个临界资源 JVM 通过 CAS 原子指令来修改_cxq 队列。
    每当有新来的节点入队,它的 next 指针总是指向之前队列的头节点,而_cxq 指针会指向该新入队的节点,所以是后来居上。

  1. _EntryList: _cxq 队列中有资格成为候选资源的线程会被移动到该队列中。

  1. _WaitSet: 等待队列因为调用 wait 方法而被阻塞的线程会被放在该队列中。

monitor 竞争过程


  1. 通过 CAS 尝试把 monitor 的 owner 字段设置为当前线程。

  1. 如果设置之前的 owner 指向当前线程,说明当前线程再次进入 monitor,即重入锁执行 recursions ++ ,记录重入的次数。

  1. 如果当前线程是第一次进入该 monitor, 设置 recursions 为 1,_owner 为当前线程,该线程成功获得锁并返回。

  1. 如果获取锁失败,则等待锁的释放。

执行 monitorenter 指令时 调用以下代码

IRT_ENTRY_NO_ASYNC(void,  InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERTthread->last_frame().interpreter_frame_verify_monitor(elem);
#endifif  (PrintBiasedLockingStatistics) {Atomic::inc(BiasedLocking::slow_path_entry_count_addr());}Handle h_obj(thread,  elem->obj());assert(Universe::heap()->is_in_reserved_or_null(h_obj()),"must  be NULL or an object");
// 是否使用偏向锁   JVM 启动时设置的偏向锁-XX:-UseBiasedLocking=false/trueif (UseBiasedLocking) {// Retry fast entry if bias is  revoked to avoid unnecessary inflationObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);} else {// 轻量级锁ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);}assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),"must be NULL or an  object");
#ifdef ASSERTthread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

slow_enter 方法主要是轻量级锁的一些操作,如果操作失败则会膨胀为重量级锁,过程前面已经描述比较清楚此处不在赘述。enter 方法则为重量级锁的入口源码如下

void ATTR  ObjectMonitor::enter(TRAPS) {Thread * const Self = THREAD ;void * cur ;// 省略部分代码// 通过 CAS 操作尝试把 monitor 的_owner 字段设置为当前线程cur = Atomic::cmpxchg_ptr (Self,  &_owner, NULL) ;if (cur == NULL)  {assert (_recursions == 0   , "invariant") ;assert (_owner      == Self, "invariant") ;return ;}// 线程重入,recursions++if (cur == Self)  {_recursions ++ ;return ;}// 如果当前线程是第一次进入该 monitor, 设置_recursions 为 1,_owner 为当前线程if  (Self->is_lock_owned ((address)cur)) {assert (_recursions == 0, "internal  state error");_recursions = 1 ;_owner = Self ;OwnerIsThread = 1 ;return ;}for (;;) {jt->set_suspend_equivalent();// 如果获取锁失败,则等待锁的释放;EnterI (THREAD) ;if  (!ExitSuspendEquivalent(jt)) break ;_recursions = 0 ;_succ = NULL ;exit (false, Self) ;jt->java_suspend_self();}Self->set_current_pending_monitor(NULL);}}

monitor 等待


  1. 当前线程被封装成 ObjectWaiter 对象 node,状态设置成ObjectWaiter::TS_CXQ。

  1. for 循环通过 CAS 把 node 节点 push 到_cxq列表中,同一时刻可能有多个线程把自己的 node 节点 push 到_cxq列表中。

  1. node 节点 push 到_cxq 列表之后,通过自旋尝试获取锁,如果还是没有获取到锁则通过 park 将当前线程挂起等待被唤醒。

  1. 当该线程被唤醒时会从挂起的点继续执行,通过ObjectMonitor::TryLock 尝试获取锁。

// 省略部分代码void ATTR ObjectMonitor::EnterI (TRAPS) {Thread * Self = THREAD ;assert (Self->is_Java_thread(), "invariant") ;assert (((JavaThread *)  Self)->thread_state() == _thread_blocked    , "invariant") ;// Try lock 尝试获取锁if (TryLock  (Self) > 0) {assert (_succ != Self              , "invariant") ;assert (_owner == Self             , "invariant") ;assert (_Responsible !=  Self       , "invariant") ;// 如果获取成功则退出,避免 park unpark 系统调度的开销return ;}// 自旋获取锁if  (TrySpin(Self) > 0) {assert (_owner == Self, "invariant");assert (_succ != Self, "invariant");assert (_Responsible != Self, "invariant");return;}// 当前线程被封装成 ObjectWaiter 对象node, 状态设置成 ObjectWaiter::TS_CXQObjectWaiter node(Self) ;Self->_ParkEvent->reset() ;node._prev   = (ObjectWaiter *) 0xBAD ;node.TState  = ObjectWaiter::TS_CXQ ;// 通过 CAS 把 node 节点 push 到_cxq 列表中ObjectWaiter * nxt ;for (;;) {node._next = nxt = _cxq ;if  (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;// 再次 tryLockif (TryLock  (Self) > 0) {assert (_succ != Self         , "invariant") ;assert (_owner == Self        , "invariant") ;assert (_Responsible !=  Self  , "invariant") ;return ;}}for (;;) {// 本段代码的主要思想和 AQS 中相似可以类比来看// 再次尝试if (TryLock  (Self) > 0) break ;assert (_owner != Self, "invariant") ;if ((SyncFlags  & 2) && _Responsible == NULL) {Atomic::cmpxchg_ptr (Self,  &_Responsible, NULL) ;}// 满足条件则 park selfif (_Responsible  == Self || (SyncFlags & 1)) {TEVENT (Inflated enter -  park TIMED) ;Self->_ParkEvent->park ((jlong) RecheckInterval) ;// Increase the  RecheckInterval, but clamp the value.RecheckInterval *= 8 ;if  (RecheckInterval > 1000) RecheckInterval  = 1000 ;} else {TEVENT (Inflated enter -  park UNTIMED) ;// 通过 park 将当前线程挂起,等待被唤醒Self->_ParkEvent->park() ;}if  (TryLock(Self) > 0) break ;// 再次尝试自旋if  ((Knob_SpinAfterFutile & 1) &&  TrySpin(Self) > 0) break;}return ;}

monitor 释放


当某个持有锁的线程执行完同步代码块时,会释放锁并 unpark 后续线程(由于篇幅只保留重要代码)。

void ATTR  ObjectMonitor::exit(bool not_suspended, TRAPS) {Thread * Self = THREAD ;if (_recursions  != 0) {_recursions--;        // this is  simple recursive enterTEVENT (Inflated exit - recursive)  ;return ;}ObjectWaiter * w = NULL ;int QMode =  Knob_QMode ;// 直接绕过 EntryList 队列,从 cxq 队列中获取线程用于竞争锁if (QMode == 2 &&  _cxq != NULL) {w = _cxq ;assert (w != NULL, "invariant") ;assert (w->TState ==  ObjectWaiter::TS_CXQ, "Invariant") ;ExitEpilog (Self, w) ;return ;}// cxq 队列插入 EntryList 尾部if (QMode == 3 &&  _cxq != NULL) {w = _cxq ;for (;;) {assert (w != NULL, "Invariant") ;ObjectWaiter * u =  (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;if (u == w) break ;w = u ;}ObjectWaiter * q = NULL ;ObjectWaiter * p ;for (p = w ; p !=  NULL ; p = p->_next) {guarantee (p->TState  == ObjectWaiter::TS_CXQ, "Invariant") ;p->TState =  ObjectWaiter::TS_ENTER ;p->_prev = q ;q = p ;}ObjectWaiter * Tail ;for (Tail =  _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail =  Tail->_next) ;if (Tail ==  NULL) {_EntryList = w ;} else {Tail->_next = w ;w->_prev = Tail ;}}// cxq 队列插入到_EntryList 头部if (QMode == 4 &&  _cxq != NULL) {// 把 cxq 队列放入 EntryList// 此策略确保最近运行的线程位于 EntryList 的头部w = _cxq ;for (;;) {assert (w != NULL, "Invariant") ;ObjectWaiter * u =  (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;if (u == w) break ;w = u ;}assert (w != NULL              , "invariant") ;ObjectWaiter * q = NULL ;ObjectWaiter * p ;for (p = w ; p !=  NULL ; p = p->_next) {guarantee (p->TState  == ObjectWaiter::TS_CXQ, "Invariant") ;p->TState =  ObjectWaiter::TS_ENTER ;p->_prev = q ;q = p ;}if (_EntryList  != NULL) {q->_next =  _EntryList ;_EntryList->_prev = q ;}_EntryList = w ;}w = _EntryList  ;if (w != NULL) {assert (w->TState ==  ObjectWaiter::TS_ENTER, "invariant") ;ExitEpilog (Self, w) ;return ;}w = _cxq ;if (w == NULL) continue ;for (;;) {assert (w != NULL, "Invariant") ;ObjectWaiter * u =  (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;if (u == w) break ;w = u ;}if (QMode == 1) {// QMode == 1 : 把 cxq 倾倒入 EntryList 逆序ObjectWaiter * s = NULL ;ObjectWaiter * t = w ;ObjectWaiter * u = NULL ;while (t != NULL) {guarantee (t->TState ==  ObjectWaiter::TS_CXQ, "invariant") ;t->TState =  ObjectWaiter::TS_ENTER ;u = t->_next ;t->_prev = u ;t->_next = s ;s = t;t = u ;}_EntryList  = s ;assert (s != NULL, "invariant") ;} else {// QMode == 0 or QMode == 2_EntryList = w ;ObjectWaiter * q = NULL ;ObjectWaiter * p ;// 将单向链表构造成双向环形链表;for (p = w ; p !=  NULL ; p = p->_next) {guarantee (p->TState ==  ObjectWaiter::TS_CXQ, "Invariant") ;p->TState =  ObjectWaiter::TS_ENTER ;p->_prev = q ;q = p ;}}if (_succ !=  NULL) continue;w = _EntryList  ;if (w != NULL) {guarantee (w->TState ==  ObjectWaiter::TS_ENTER, "invariant") ;ExitEpilog (Self, w) ;return ;}}}

notify 唤醒


notify 或者 notifyAll 方法可以唤醒同一个锁监视器下调用 wait 挂起的线程,具体实现如下

void  ObjectMonitor::notify(TRAPS) {CHECK_OWNER();if (_WaitSet ==  NULL) {TEVENT (Empty - Notify);return;}DTRACE_MONITOR_PROBE(notify, this, object(),  THREAD);int Policy = Knob_MoveNotifyee;Thread::SpinAcquire(&_WaitSetLock, "WaitSet -  notify");ObjectWaiter *iterator =  DequeueWaiter();if (iterator !=  NULL) {// 省略一些代码// 头插 EntryListif (Policy == 0) {if (List ==  NULL) {iterator->_next =  iterator->_prev = NULL;_EntryList = iterator;} else {List->_prev =  iterator;iterator->_next =  List;iterator->_prev =  NULL;_EntryList = iterator;}} else if (Policy == 1) {      // 尾插 EntryListif (List ==  NULL) {iterator->_next =  iterator->_prev = NULL;_EntryList = iterator;} else {ObjectWaiter *Tail;for (Tail = List;  Tail->_next != NULL; Tail = Tail->_next);assert (Tail != NULL  && Tail->_next == NULL, "invariant");Tail->_next =  iterator;iterator->_prev =  Tail;iterator->_next = NULL;}} else if (Policy == 2) {      // 头插 cxq// prepend to  cxqif (List ==  NULL) {iterator->_next =  iterator->_prev = NULL;_EntryList = iterator;} else {iterator->TState =  ObjectWaiter::TS_CXQ;for (;;) {ObjectWaiter *Front  = _cxq;iterator->_next  = Front;if  (Atomic::cmpxchg_ptr(iterator, &_cxq, Front) == Front) {break;}}}} else if (Policy == 3) {      // 尾插 cxqiterator->TState =  ObjectWaiter::TS_CXQ;for (;;) {ObjectWaiter *Tail;Tail = _cxq;if (Tail ==  NULL) {iterator->_next  = NULL;if  (Atomic::cmpxchg_ptr(iterator, &_cxq, NULL) == NULL) {break;}} else {while  (Tail->_next != NULL) Tail = Tail->_next;Tail->_next =  iterator;iterator->_prev  = Tail;iterator->_next  = NULL;break;}}} else {ParkEvent *ev =  iterator->_event;iterator->TState =  ObjectWaiter::TS_RUN;OrderAccess::fence();ev->unpark();}if (Policy < 4) {iterator->wait_reenter_begin(this);}}// 自旋释放Thread::SpinRelease(&_WaitSetLock);if (iterator !=  NULL && ObjectMonitor::_sync_Notifications != NULL) {ObjectMonitor::_sync_Notifications->inc();}}
  • 偏向锁:通过对比 Mark Word thread id 解决加锁问题。

  • 轻量级锁:是通过用 CAS 操作 Mark Word 和自旋来解决加锁问题,避免线程阻塞和唤醒而影响性能。

  • 重量级锁:是将除了拥有锁的线程以外的线程都阻塞。

相关文章:

锁升级之Synchronized

Synchronized JVM系统锁一个对象里如果有多个synchronized方法&#xff0c;同一时刻&#xff0c;只要有一个线程去调用其中的一个synchronized方法&#xff0c;其他线程只能等待&#xff01;锁的是当前对象&#xff0c;对象被锁定后&#xff0c;其他线程都不能访问当前对象的其…...

基于nodejs+vue疫情网课管理系统

疫情网课也都将通过计算机进行整体智能化操作,对于疫情网课管理系统所牵扯的管理及数据保存都是非常多的,例如管理员&#xff1a;首页、个人中心、学生管理、教师管理、班级管理、课程分类管理、课程表管理、课程信息管理、作业信息管理、请假信息管理、上课签到管理、论坛交流…...

Zabbix 构建监控告警平台(三)

Zabbix User parametersZabbix Trigger1.Zabbix User parameters 1.1即自定义KEY 注意&#xff1a;mysql安装在被监测主机 [rootlocalhost ~]# yum -y install mariadb-server mariadb [rootlocalhost ~]# systemctl start mariadb [rootlocalhost ~]# mysqladmin -uroot statu…...

Linux系统之dool命令行工具的基本使用

Linux系统之dool命令行工具的基本使用一、dool命令行工具介绍二、本地系统环境检查1.检查系统版本2.检查系统内核版本三、下载dool软件包1.创建下载目录2.下载dool四、安装dool1.安装python32.安装dool五、dool的命令帮助六、dool的基本使用1.直接使用dool监控系统2.监控cpu和网…...

LeetCode-2335-装满杯子需要的最短总时长

1、堆 我们可以维护一个堆&#xff0c;首先我们将数组中不为0的数全部加入堆中&#xff0c;而后进行循环。当堆不为空时&#xff0c;我们将堆顶元素出堆并减一&#xff0c;而后观察是否还能继续出堆&#xff0c;若能则出堆&#xff0c;否则跳过&#xff0c;最后我们将处理后的…...

npm ERR! code ELIFECYCLE解决方案,npm犯错!myweb@1.0.0构建脚本失败。

1.问题npm ERR! code ELIFECYCLEnpm ERR! errno 1npm ERR! myweb1.0.0 build: webpack --config config/webpack.config.jsnpm ERR! Exit status 1npm ERR!npm ERR! Failed at the myweb1.0.0 build script.npm犯错!代码ELIFECYCLEnpm犯错!errno 1npm犯错!myweb1.0.0 build: we…...

最小二乘支持向量机”在学习偏微分方程 (PDE) 解方面的应用(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 本代码说明了“最小二乘支持向量机”在学习偏微分方程 &#xff08;PDE&#xff09; 解方面的应用。提供了一个示例&#xff0c…...

ISYSTEM调试实践8-winIDEA Analyzer功能1

前面几篇介绍了ISYSTEM的基本调试界面和功能&#xff0c;相比我之前用过的IDE&#xff0c;除了几种断点方式和脚本功能以外&#xff0c;应该都是比较简单&#xff0c;稍微操作一下就可以直接上手&#xff0c;后续我将介绍winIDEA的Analyzer 功能。 1 Analyzer简介 iSYSTEM An…...

每日学术速递2.11

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.IR、cs.MM 1.A Comprehensive Survey on Multimodal Recommender Systems: Taxonomy, Evaluation, and Future Directions 标题&#xff1a;关于多模态推荐系统的综合调查&#xff1a;分…...

宝塔搭建实战php开源likeadmin通用管理admin端vue3源码(二)

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 上一期给大家分享了server端的部署方式&#xff0c;今天来给大家分享admin端在本地搭建&#xff0c;与打包发布到宝塔的方法。感兴趣的朋友可以自行下载学习。 技术架构 vscode node16 vue3 elementPlus vit…...

网络基础-虚拟化工具-网桥

系列文章目录 本系列文章主要是回顾和学习工作中常用的网络基础命令&#xff0c;在此记录以便于回顾。 该篇文章主要是讲解虚拟化的工具网桥相关的概念和常用命令 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录系…...

剑指 Offer 14- II. 剪绳子 II

剑指 Offer 14- II. 剪绳子 II 给你一根长度为 n 的绳子&#xff0c;请把绳子剪成整数长度的 m 段&#xff08;m、n都是整数&#xff0c;n>1并且m>1&#xff09;&#xff0c;每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少&a…...

English Learning - Day55 作业打卡 2023.2.9 周四

English Learning - Day55 作业打卡 2023.2.9 周四引言1. Jim 在看电视的时候他的老婆正在做饭。2. 他刚睡着电话就响了。3. 我正在想事情&#xff0c;这时忽然有人从后面抓我胳膊。4. 我们总是边吃火锅边唱歌。5. 他一听说出了事故&#xff0c;马上就来了现场。6. He entered …...

pixhawk2.4.8-地面站配置-APM固件

文章目录一、硬件准备二、软件准备1 已实飞测试2 MP地面站 任意版本下载&#xff1a;3 APM固件 任意版本下载&#xff1a;三、飞控校准1 刷固件2 机架选择3 加速度计校准4 指南针校准5 遥控器校准6 飞行模式7 紧急断电&无头模式8 基础参数设置9 电流计校准10 电调校准11 起…...

golang 通道类型

文章目录一、什么是通道类型二、通道产生的原因三、声明channel四、创建channel五、channel相关操作1、发送值2、接收值3、关闭通道3.1 注意3.2 特点四、通道类型1、无缓冲通道2、有缓冲通道五、单向通道一、什么是通道类型 Go 语言中的通道&#xff08;channel&#xff09;是一…...

并发、并行、吞吐量、延迟、响应时间 含义理解

并发、并行、吞吐量、延迟、响应时间 知识点了解 1. 响应时间(RT) 理解&#xff1a;响应时间是指系统对请求作出响应的时间。例如一个正在运行的服务&#xff0c;服务内程序接受到参数请求开始&#xff0c;到程序计算完&#xff0c;并将结果返回出去结束&#xff0c;这段时间…...

HTTP 和 HTTPS 的区别

文章目录前言一、HTTP 与 HTTPS 的基本概念HTTPHTTPS二、HTTP 和 HTTPS协议的区别前言 浏览网站时&#xff0c;我们会发现网址有两种格式&#xff0c;一种以http://开头&#xff0c;一种https://开头。好像这两种格式差别不大&#xff0c;只多了一个s&#xff0c;实际上他们有…...

微搭低代码从入门到精通07-基础布局组件

低码开发不同于传统开发&#xff0c;传统开发我们通常需要编写前端代码和后端代码。前端代码由HTML、CSS和JavaScript组成&#xff0c;后端代码我们通常要用后端语言比如Java来编写接口。 低码开发的特点是可视化开发&#xff0c;在编辑器中通过组件的拖拽来完成页面的编制。如…...

Docker镜像的创建

Docker镜像Docker镜像Docker 镜像是一个特殊的文件系统提供容器运行时所需的程序、库、资源、配置等文件包含一些为运行时准备的一些配置参数&#xff08;如匿名卷、环境变量、用户等&#xff09;镜像不包含任何动态数据&#xff0c;其内容在构建之后也不会被改变。Docker镜像的…...

电子技术——MOS差分输入对

电子技术——MOS差分输入对 差分输入系统因其极高的共模抑制能力&#xff0c;差分输入几乎是是构建所有通用模拟IC的基本前级输入&#xff0c;也是现代信号传输理论的基础。本节我们讲解MOS差分输入对。 MOS差分输入对 下图展示了MOS差分输入对的基本原理图&#xff1a; 一个…...

树莓派 - 小记

文章目录关于树莓派Raspberry Pi OSGPIOScratch 编程Minecraft相关硬件关于树莓派 树莓派&#xff1a;Raspberry Pi&#xff0c;由美国树莓派基金会开发&#xff0c;是一款专门用于计算机教育的极简计算机。 第一代发布于 2012年。 特点&#xff1a;精致小巧&#xff0c;价格低…...

【论文解读|KDD2020】AKT. Context-Aware Attentive Knowledge Tracing

文章目录摘要1 引言1.1 贡献3 模型3.4 基于Rasch模型的嵌入5 结论摘要 知识追踪(KT)是指根据学习者在教育应用中的过去表现预测未来学习者表现的问题。KT最近使用灵活的基于深度神经网络的模型的发展在这一任务中表现出色。然而&#xff0c;这些模型通常提供有限的可解释性&am…...

Geek Uninstaller:向流氓软件火力全开,超良心的软件彻底卸载工具

写在前面 我们在电脑上安装软件&#xff0c;以及在使用软件的过程中&#xff0c;会产生一些程序文件、注册表项和临时文件等&#xff0c;用来支持软件的正常使用&#xff0c;都是正常现象。 但是&#xff0c;在卸载软件时&#xff0c;很多软件自身的卸载程序很不负责任&#…...

Java线程池

什么是线程池 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合&#xff0c;然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而&#xff0c;增加可用线程数量是可能的。线程池中的每…...

2023-02-10 - 5 文本搜索

与其他需要精确匹配的数据不同&#xff0c;文本数据在前期的索引构建和搜索环节都需要进行额外的处理&#xff0c;并且在匹配环节还要进行相关性分数计算。本章将详细介绍文本搜索的相关知识。 本章首先从总体上介绍文本的索引建立过程和搜索过程&#xff0c;然后介绍分析器的…...

华为OD机试 - 最近的医院(Python),简单直白

任务混部 | 华为 OD 机试【最新】 题目 新型冠状病毒疫情的肆虐,使得家在武汉的大壮不得不思考自己家和附近定点医院的具体情况。 经过一番调查, 大壮明白了距离自己家最近的定点医院有两家。其中医院 A 距离自己的距离是 X 公里,医院 B 距离自己的距离是 Y 公里。 由于…...

Leetcode.1223 掷骰子模拟

题目链接 Leetcode.1223 掷骰子模拟 Rating &#xff1a; 2008 题目描述 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。 不过我们在使用它时有个约束&#xff0c;就是使得投掷骰子时&#xff0c;连续 掷出数字 i 的次数不能超过 rollMax[i]&#xff08;i 从 1…...

数据分析到底该怎么学呢?讲真,真不难!

这几年&#xff0c;“数据分析”是很火啊&#xff0c;在这个数据驱动一切的时代&#xff0c;数据挖掘和数据分析就是这个时代的“淘金”&#xff0c;懂数据分析、拥有数据思维&#xff0c;往往成了大厂面试的加分项。 比如通过数据分析&#xff0c;我们可以更好地了解用户画像…...

活动星投票紫砂新青年制作一个投票活动

“紫砂新青年”网络评选投票_免费链接投票_作品投票通道_扫码投票怎样进行现在来说&#xff0c;公司、企业、学校更多的想借助短视频推广自己。通过微信投票小程序&#xff0c;网友们就可以通过手机拍视频上传视频参加活动&#xff0c;而短视频微信投票评选活动既可以给用户发挥…...

Git | 在IDEA中使用Git

目录 一、在IDEA中配置Git 1.1 配置Git 1.2 获取Git仓库 1.3 将本地项目推送到远程仓库 1.4 .gitignore文件的作用 二、本地仓库操作 2.1 将文件加入暂存区 2.2 将暂存区的文件提交到版本库 2.3 查看日志 三、远程仓库操作 3.1 查看和添加远程仓库 3.2 推送至远程仓…...