哪个网站可以做店招店标轮播泰州专业网站建设公司
2026/2/21 19:50:15 网站建设 项目流程
哪个网站可以做店招店标轮播,泰州专业网站建设公司,建设局网站项目负责人资质要求,wordpress整站搬家手写阻塞队列的线程安全设计#xff1a;这段 MyBlockingQueue 到底“安全”在哪#xff1f; 这份 MyBlockingQueue 是典型的循环数组 阻塞语义实现#xff1a;data[] 存元素#xff0c;head/tail 控制出队/入队位置#xff0c;size 记录当前元素个数。并发场景里#xf…手写阻塞队列的线程安全设计这段MyBlockingQueue到底“安全”在哪这份MyBlockingQueue是典型的循环数组 阻塞语义实现data[]存元素head/tail控制出队/入队位置size记录当前元素个数。并发场景里“线程安全”要同时满足两件事数据结构不被写坏任何时刻都要维持队列不变量0 size capacityhead/tail始终落在合法范围内入队不会覆盖未消费元素出队不会重复消费同一元素。阻塞语义正确满了put必须等空了take必须等并且等待要能被可靠唤醒唤醒后行为仍然正确。下面按代码里的关键点逐个拆开讲哪些地方做了线程安全处理、为什么必须这么做。源代码classMyBlockingQueue{privateString[]datanull;privateinthead0;privateinttail0;privateintsize0;publicMyBlockingQueue(intcapacity){datanewString[capacity];}publicvoidput(Stringelem)throwsInterruptedException{synchronized(this){while(sizedata.length){//这里用while不用if的原因跟下面一样//队列满了需要阻塞//return;this.wait();}data[tail]elem;tail;if(taildata.length){tail0;}size;this.notify();}}publicStringtake()throwsInterruptedException{synchronized(this){while(size0){//return null;this.wait();//用while不用if是为了多次验证当前这里的条件是否成立//wait唤醒之前跟之后都判定一次,主要目的在之后这一次}Stringretdata[head];head;if(headdata.length){head0;}size--;this.notify();returnret;}}}1synchronized (this)把共享状态的读写变成互斥的“原子区间”put()和take()的核心逻辑都包在synchronized(this){...}这一步是线程安全的“地基”。原因在于入队/出队都不是单条指令而是一串复合操作检查条件满/空写/读数组槽位移动head/tail并处理回环修改size唤醒等待线程如果没有互斥两个线程交错执行会出现非常具体的灾难丢失更新两个线程同时size或size--最终只生效一次覆盖数据两个生产者同时写入同一个tail位置覆盖彼此元素重复消费两个消费者同时从同一个head位置取值指针错乱head/tail推进时被打断导致结构性破坏队列不变量失真synchronized (this)的意义就是同一时刻只允许一个线程修改data/head/tail/size这组共享状态把“复合操作”提升为一个对外不可分割的临界区从而保证原子性与一致性。2wait()满/空时阻塞等待并且关键地——释放锁put 里的等待队列满while(sizedata.length){this.wait();}take 里的等待队列空while(size0){this.wait();}这里的线程安全点不只是“阻塞”而是wait()的组合语义当前线程进入等待状态释放当前持有的这把锁this 的监视器锁之后被唤醒时会先重新竞争锁拿到锁后才继续执行释放锁这件事至关重要如果队列满时生产者不释放锁消费者永远进不来做take()队列永远满如果队列空时消费者不释放锁生产者永远进不来做put()队列永远空。于是就变成“抱着锁睡觉”全员卡死。所以wait()是一种条件同步不满足条件时把机会让出去让别的线程进入临界区改变条件。顺带一提wait()必须在持有该对象监视器锁的情况下调用也就是必须在对应synchronized内否则会直接抛异常。这里把wait放在同步块内是正确姿势。3while不用if被唤醒 ≠ 条件已经满足代码里两处都坚持用whileputwhile (size data.length) wait()takewhile (size 0) wait()这不是“风格问题”而是正确性底线。原因很现实线程从wait()醒来后并不是立刻继续执行而是要重新抢锁。在重新抢锁、以及重新进入临界区之前队列状态可能已经被其他线程改过了。于是“醒来时的世界”不一定还是“刚被唤醒时以为的世界”。再说得更直接一点可能发生虚假唤醒spurious wakeup线程莫名其妙醒了但条件没变多线程场景下可能被唤醒的那一刻条件短暂满足抢到锁时又不满足了notifyAll场景下会唤醒一批线程其中只有少数真的能继续其余必须回去再等因此正确模板永远是条件谓词 while 循环等待。醒来后再检查一次条件条件不满足就继续wait()这样才能保证不变量不被破坏。4notify()状态改变后“叫醒”对方继续推进在put()成功入队后size;this.notify();在take()成功出队后size--;this.notify();这一块做的是“唤醒机制”当队列状态发生变化空→非空 或 满→非满需要通知在该锁对象上等待的线程否则可能出现队列已经有元素了消费者还一直睡着死等队列已经有空位了生产者还一直睡着死等并且notify()放在同步块内、且在状态修改之后调用也是关键必须先把size/head/tail/data变更落实唤醒线程抢到锁后才能看到正确状态唤醒并不等于立刻放锁只有退出synchronized后才会真正释放锁让被唤醒线程继续5这份实现“安全”到什么程度一个典型改进点notifyvsnotifyAll这份实现靠synchronized while(wait) notify已经能保证基本正确性但在“多生产者 多消费者”更通用的场景里有一个经常被拿来打磨的点用notifyAll()替代notify()。为什么notify()可能不够稳notify()只随机唤醒一个等待线程而等待线程可能是两类生产者在等“队列非满”消费者在等“队列非空”如果现在发生的是“入队一次”队列从空变为非空理论上应该唤醒消费者但notify()有可能恰好唤醒了某个生产者。生产者醒来后会因为while (size capacity)条件仍不满足而继续睡回去这虽然不会破坏正确性因为用了 while但会造成更多无效唤醒与竞争吞吐下降甚至出现“体感卡顿”。notifyAll()的价值notifyAll()会唤醒所有在这把锁上等待的线程让它们重新竞争锁最终只有条件满足的线程能通过while检查进入临界区其余会继续等待。由于 while 做了二次校验正确性仍然稳。把两处改成更通用的写法// put 成功后this.notifyAll();// take 成功后this.notifyAll();结论notifyAll()往往更“稳”代价是可能唤醒更多线程产生额外上下文切换notify()更“省”但在多生产者多消费者混合等待下更依赖运气。6额外的小工程点不影响主线但值得顺手提这些不属于“线程安全核心”但属于“把实现打磨得更像标准库”的细节take()取走元素后可以把槽位置data[head] null;避免旧引用滞留尤其泛型对象时更有意义目前实现固定String[]工程里更常写成E[]泛型队列只用一把锁this能正确工作但更复杂的实现会把“非空/非满”拆成不同条件队列用ReentrantLock Condition减少无效唤醒提高吞吐总结这份阻塞队列之所以能在并发场景下保持正确关键防线集中在四件事互斥synchronized (this)把共享状态的复合修改变成原子临界区条件同步队列满/空时用wait()阻塞并释放锁让对方改变条件循环校验用while重试条件防止虚假唤醒与竞争下的状态漂移唤醒推进状态变化后notify/notifyAll叫醒等待线程继续工作流转

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询