【面试】如果把线程当作一个人来对待,所有问题都瞬间明白了

百度视频另类外推 黑帽SEO 黑帽SEO 立即下载

本文公众号来源:编程新说李新杰 作者:编程新说


分享一篇好文!文章含有常见的面试题!

多线程的问题都曾经困扰过每个开发人员,今天将从全新视角来解说,希望读者都能明白。

强烈建议去运行下文章中的示例代码,自己体会下。



问题究竟出在哪里?


一个线程执行,固然是安全的,但是有时太慢了,怎么办?

老祖宗告诉我们,“一方有难,八方支援”,那不就是多叫几个线程来帮忙嘛,好办呀,多new几个不就行了,又不要钱。这样能管用吗?继续往下看。

俗话说,“在家靠父母,出门靠朋友”。有了朋友的帮助,就会事半功倍。是这样的吗?

不一定,如果朋友“不靠谱”,结果竟是在“添乱”。于是就演变为,“不怕神一样的对手,就怕猪一样的队友”。可见“人多力量大”纵然是对的,但也要配合好才能成事。

人和人是朋友,那线程和线程也是“朋友”,如果多线程之间不能配合好的话,最终也会变为“猪一样的队友”。事实证明,这也不是一件易事。且容我慢慢道来。

开发是一门技术,管理是一门艺术。也许你正想带着兄弟们大干一场,可偏偏就有人要辞职。或者你付出了这么多,但别人从来没有感动过。为什么会这样呢?

因为你面对的是人。每个人都是独立的个体,有思想,有灵魂,有情感,有三观。能够接受外界的“输入”,经过“处理”后,能够产生“输出”。

说白了就是会自主的分析问题,并做出决定。这叫什么呢?答案就是,主观能动性。

拥有主观能动性的物体(比如人),你需要和它协商着或配合着来共同完成一件事情,而不能“强迫”它去做什么,因为这样往往不会有好的结果。

费了这么多口舌,就是希望把问题尽量的简单化。终于可以回到程序了,那线程的情况是不是类似的呢?答案是肯定的。

一个线程准备好后,经过CPU的调度,就可以自主的运行了。此时它俨然成了一个独立的个体,且具有主观能动性。

这本是一件好事,但却也有不好的一面,那就是你对它的“掌控”能力变弱了,颇有一种“将在外,君命有所不受”的感觉。

可能你不同意这种看法,说我可以“强迫”它停止运行,调用Thread类的stop()方法来直接把它“掐死”,不好意思,该方法已废弃。

因为线程可能在运行一些“关键”代码(比如转账),此刻不能被终止。Thread类还有一些其它的方法也都废弃了,大抵原因其实都差不多。

讲了这么多,相信你已经明白了,简单总结一下:

事情起因:线程可以独立自主的运行,可以认为它具有主观能动性。

造成结果:对它的掌控能力变弱了,而且又不能直接把它“干掉”。

解决方案:凡事商量着来,互相配合着把事情完成。

作者观点:其实就是把线程当作人来对待。



小试牛刀一下


一旦把线程当成人,就来到了人类的世界,这我们太熟悉了,所以很多问题都会变得非常简单明了。一起来看看吧。


场景一,停止

“大胖,大胖,12点了,该去吃饭了,别写了”

“好的,好的,稍等片刻,把这几行代码写完就走”

要点:把停止的信号传达给别人,别人处理完手头的事情就自己主动停止了。


 
  static void stopByFlag() {	
    ARunnable ar = new ARunnable();	
    new Thread(ar).start();	
    ar.tellToStop();	
  }	
  	
  static class ARunnable implements Runnable {	
	
    volatile boolean stop;	
    	
    void tellToStop() {	
      stop = true;	
    }	
    	
    @Override	
    public void run() {	
      println("进入不可停止区域 1。。。");	
      doingLongTime(5);	
      println("退出不可停止区域 1。。。");	
      println("检测标志stop = %s", String.valueOf(stop));	
      if (stop) {	
        println("停止执行");	
        return;	
      }	
      println("进入不可停止区域 2。。。");	
      doingLongTime(5);	
      println("退出不可停止区域 2。。。");	
    }	
    	
  }

解说:线程在预设的地点检测flag,来决定是否停止。


场景二,暂停/恢复

“大胖,大胖,先别发请求了,对方服务器快挂了”

“好的,好的,等这个执行完就不发了”

过了一会

“大胖,大胖,可以重新发请求了”

“好的,好的”

要点:把暂停的信号传达给别人,别人处理完手头的事情就自己主动暂停了。但是恢复是无法自主进行的,只能由操作系统来恢复线程的执行。



 
  static void pauseByFlag() {	
    BRunnable br = new BRunnable();	
    new Thread(br).start();	
    br.tellToPause();	
    sleep(8);	
    br.tellToResume();	
  }	
  	
  static class BRunnable implements Runnable {	
    	
    volatile boolean pause;	
    	
    void tellToPause() {	
      pause = true;	
    }	
    	
    void tellToResume() {	
      synchronized (this) {	
        this.notify();	
      }	
    }	
    	
    @Override	
    public void run() {	
      println("进入不可暂停区域 1。。。");	
      doingLongTime(5);	
      println("退出不可暂停区域 1。。。");	
      println("检测标志pause = %s", String.valueOf(pause));	
      if (pause) {	
        println("暂停执行");	
        try {	
          synchronized (this) {	
            this.wait();	
          }	
        } catch (InterruptedException e) {	
          e.printStackTrace();	
        }	
        println("恢复执行");	
      }	
      println("进入不可暂停区域 2。。。");	
      doingLongTime(5);	
      println("退出不可暂停区域 2。。。");	
    }	
    	
  }

解说:还是在预设的地点检测flag。然后就是wait/notify配合使用。


场景三,插队

“大胖,大胖,让我站到你前面,不想排队了”

“好吧”

要点:别人插队到你前面,必须等他完事后才轮到你。



 
  static void jqByJoin() {	
    CRunnable cr = new CRunnable();	
    Thread t = new Thread(cr);	
    t.start();	
    sleep(1);	
    try {	
      t.join();	
    } catch (InterruptedException e) {	
      e.printStackTrace();	
    }	
    println("终于轮到我了");	
  }	
  	
  static class CRunnable implements Runnable {	
	
    @Override	
    public void run() {	
      println("进入不可暂停区域 1。。。");	
      doingLongTime(5);	
      println("退出不可暂停区域 1。。。");	
    }	
    	
  }

解说:join方法可以让某个线程插到自己前面,等它执行完,自己才会继续执行。


场景四,叫醒

“大胖,大胖,醒醒,醒醒,看谁来了”

“谁啊,我去”

要点:要把别人从睡梦中叫醒,一定要采取稍微暴力一点的手段。


 
  static void stopByInterrupt() {	
    DRunnable dr = new DRunnable();	
    Thread t = new Thread(dr);	
    t.start();	
    sleep(2);	
    t.interrupt();	
  }	
  	
  static class DRunnable implements Runnable {	
	
    @Override	
    public void run() {	
      println("进入暂停。。。");	
      try {	
        sleep2(5);	
      } catch (InterruptedException e) {	
        println("收到中断异常。。。");	
        println("做一些相关处理。。。");	
      }	
      println("继续执行或选择退出。。。");	
    }	
    	
  }

解说:线程在sleep或wait时,是处于无法交互的状态的,此时只能使用interrupt方法中断它,线程会被激活并收到中断异常。



常见的协作配合


上面那些场景,其实都是对一个线程的操作,下面来看多线程间的一些配合。

事件一,考试

假设今天考试,20个学生,1个监考老师。规定学生可以提前交卷,即把卷子留下,直接走人就行了。

但老师必须等到所有的学生都走后,才可以收卷子,然后装订打包。

如果把学生和老师都看作线程,就是1个线程和20个线程的配合问题,即等20个线程都结束了,这1个线程才开始。

比如20个线程分别在计算数据,等它们都结束后得到20个中间结果,最后这1个线程再进行后续汇总、处理等。


 
  static final int COUNT = 20;	
  	
  public static void main(String[] args) throws Exception {	
    new Thread(new Teacher(cdl)).start();	
    sleep(1);	
    for (int i = 0; i < COUNT; i++) {	
      new Thread(new Student(i, cdl)).start();	
    }	
    synchronized (ThreadCo1.class) {	
      ThreadCo1.class.wait();	
    }	
  }	
  	
  static CountDownLatch cdl = new CountDownLatch(COUNT);	
  	
  static class Teacher implements Runnable {	
    	
    CountDownLatch cdl;	
    	
    Teacher(CountDownLatch cdl) {	
      this.cdl = cdl;	
    }	
    	
    @Override	
    public void run() {	
      println("老师发卷子。。。");	
      try {	
        cdl.await();	
      } catch (InterruptedException e) {	
        e.printStackTrace();	
      }	
      println("老师收卷子。。。");	
    }	
    	
  }	
  	
  static class Student implements Runnable {	
    	
    CountDownLatch cdl;	
    int num;	
    	
    Student(int num, CountDownLatch cdl) {	
      this.num = num;	
      this.cdl = cdl;	
    }	
    	
    @Override	
    public void run() {	
      println("学生(%d)写卷子。。。", num);	
      doingLongTime();	
      println("学生(%d)交卷子。。。", num);	
      cdl.countDown();	
    }	
    	
  }

解说:每完成一个线程,计数器减1,当减到0时,被阻塞的线程自动执行。


事件二,旅游

最近景色宜人,公司组织去登山,大伙都来到了山脚下,登山过程自由进行。

但为了在特定的地点拍集体照,规定1个小时后在半山腰集合,谁最后到的,要给大家表演一个节目。

然后继续登山,在2个小时后,在山顶集合拍照,还是谁最后到的表演节目。

接着开始下山了,在2个小时后在山脚下集合,点名回家,最后到的照例表演节目。


 
  static final int COUNT = 5;	
  	
  public static void main(String[] args) throws Exception {	
    for (int i = 0; i < COUNT; i++) {	
      new Thread(new Staff(i, cb)).start();	
    }	
    synchronized (ThreadCo2.class) {	
      ThreadCo2.class.wait();	
    }	
  }	
  	
  static CyclicBarrier cb = new CyclicBarrier(COUNT, new Singer());	
  	
  static class Singer implements Runnable {	
    	
    @Override	
    public void run() {	
      println("为大家唱歌。。。");	
    }	
    	
  }	
  	
  static class Staff implements Runnable {	
    	
    CyclicBarrier cb;	
    int num;	
    	
    Staff(int num, CyclicBarrier cb) {	
      this.num = num;	
      this.cb = cb;	
    }	
    	
    @Override	
    public void run() {	
      println("员工(%d)出发。。。", num);	
      doingLongTime();	
      println("员工(%d)到达地点一。。。", num);	
      try {	
        cb.await();	
      } catch (Exception e) {	
        e.printStackTrace();	
      }	
      println("员工(%d)再出发。。。", num);	
      doingLongTime();	
      println("员工(%d)到达地点二。。。", num);	
      try {	
        cb.await();	
      } catch (Exception e) {	
        e.printStackTrace();	
      }	
      println("员工(%d)再出发。。。", num);	
      doingLongTime();	
      println("员工(%d)到达地点三。。。", num);	
      try {	
        cb.await();	
      } catch (Exception e) {	
        e.printStackTrace();	
      }	
      println("员工(%d)结束。。。", num);	
    }	
    	
  }


解说:某个线程到达预设点时就在此等待,等所有的线程都到达时,大家再一起向下个预设点出发。如此循环反复下去。


事件三,劳动

大胖和小白去了创业公司,公司为了节约开支,没有请专门的保洁人员。让员工自己扫地和擦桌。

大胖觉得擦桌轻松,就让小白去扫地。可小白觉得扫地太累,也想擦桌。

为了公平起见,于是决定,每人先干一半,然后交换工具,再接着干对方剩下的那一个半。


 
  public static void main(String[] args) throws Exception {	
    new Thread(new Staff("大胖", new Tool("笤帚", "扫地"), ex)).start();	
    new Thread(new Staff("小白", new Tool("抹布", "擦桌"), ex)).start();	
    synchronized (ThreadCo3.class) {	
      ThreadCo3.class.wait();	
    }	
  }	
  	
  static Exchanger<Tool> ex = new Exchanger<>();	
  	
  static class Staff implements Runnable {	
    	
    String name;	
    Tool tool;	
    Exchanger<Tool> ex;	
    	
    Staff(String name, Tool tool, Exchanger<Tool> ex) {	
      this.name = name;	
      this.tool = tool;	
      this.ex = ex;	
    }	
    	
    @Override	
    public void run() {	
      println("%s拿的工具是[%s],他开始[%s]。。。", name, tool.name, tool.work);	
      doingLongTime();	
      println("%s开始交换工具。。。", name);	
      try {	
        tool = ex.exchange(tool);	
      } catch (Exception e) {	
        e.printStackTrace();	
      }	
      	
      println("%s的工具变为[%s],他开始[%s]。。。", name, tool.name, tool.work);	
    }	
    	
  }	
  	
  static class Tool {	
    	
    String name;	
    String work;	
    	
    Tool(String name, String work) {	
      this.name = name;	
      this.work = work;	
    }	
    	
  }

解说:两个线程在预设点交换变量,先到达的等待对方。


事件四,魔性游戏

这是一个充满魔性的小游戏,由一个团队一起参加。所有人每隔5秒钟抽一次签,每个人有50%的概率留下来或被淘汰。

留下来的人下次抽签时同样有50%的概率被淘汰。被淘汰的人下次抽签时同样有50%的概率复活。

团队所有成员都被淘汰完,为挑战失败,团队所有成员都回到游戏中(除刚开始外),为挑战成功。

比如一开始10人参与游戏,第一轮抽签后,6人留下,4人淘汰。

第二轮抽签后,留下的6人中4人被淘汰,淘汰的4人中2人复活,那么目前是4人在游戏中,6人被淘汰。

一直如此继续下去,直到10人全部被淘汰,或全部回到游戏中。

可见,人数越多,全部被淘汰的概率越小,但全部回到游戏中的概率也越小。

反之,人数越少,全部回到游戏中的概率越大,但全部被淘汰的概率也越大。

是不是很有魔性啊。哈哈。


 
  static final int COUNT = 6;	
  	
  public static void main(String[] args) throws Exception {	
    new Thread(new Challenger("张三")).start();	
    new Thread(new Challenger("李四")).start();	
    new Thread(new Challenger("王五")).start();	
    new Thread(new Challenger("赵六")).start();	
    new Thread(new Challenger("大胖")).start();	
    new Thread(new Challenger("小白")).start();	
    synchronized (ThreadCo4.class) {	
      ThreadCo4.class.wait();	
    }	
  }	
  	
  static Phaser ph = new Phaser() {	
    	
    protected boolean onAdvance(int phase, int registeredParties) {	
      println2("第(%d)局,剩余[%d]人", phase, registeredParties);	
      return registeredParties == 0 ||	
          (phase != 0 && registeredParties == COUNT);	
    };	
  };	
  	
  static class Challenger implements Runnable {	
    	
    String name;	
    int state;	
    	
    Challenger(String name) {	
      this.name = name;	
      this.state = 0;	
    }	
    	
    @Override	
    public void run() {	
      println("[%s]开始挑战。。。", name);	
      ph.register();	
      int phase = 0;	
      int h;	
      while (!ph.isTerminated() && phase < 100) {	
        doingLongTime(5);	
        if (state == 0) {	
          if (Decide.goon()) {	
            h = ph.arriveAndAwaitAdvance();	
            if (h < 0)	
              println("No%d.[%s]继续,但已胜利。。。", phase, name);	
            else	
              println("No%d.[%s]继续at(%d)。。。", phase, name, h);	
          } else {	
            state = -1;	
            h = ph.arriveAndDeregister();	
            println("No%d.[%s]退出at(%d)。。。", phase, name, h);	
          }	
        } else {	
          if (Decide.revive()) {	
            state = 0;	
            h = ph.register();	
            if (h < 0)	
              println("No%d.[%s]复活,但已失败。。。", phase, name);	
            else	
              println("No%d.[%s]复活at(%d)。。。", phase, name, h);	
          } else {	
            println("No%d.[%s]没有复活。。。", phase, name);	
          }	
        }	
        phase++;	
      }	
      if (state == 0) {	
        ph.arriveAndDeregister();	
      }	
      println("[%s]结束。。。", name);	
    }	
    	
  }	
  	
  static class Decide {	
	
    static boolean goon() {	
      return random(9) > 4;	
    }	
    	
    static boolean revive() {	
      return random(9) < 5;	
    }	
  }


解说:某个线程到达预设点后,可以选择等待同伴或自己退出,等大家都到达后,再一起向下一个预设点出发,随时都可以有新的线程加入,退出的也可以再次加入。




生产与销售的问题


在现实中,工厂生产出来的产品会先放到仓库存储,销售人员签了单子后,会从仓库把产品发给客户。

如果生产的过快,仓库里产品越堆越多,直到把仓库堆满,那就必须停止生产,因为没地方放了。

此时只能让销售人员赶紧出去签单子,把产品发出去,仓库就有了空间,可以恢复生产了。

如果销售的过快,仓库里产品越来越少,直到把仓库清空,那就必须停止销售,因为没产品了。

此时只能让生产人员赶紧生产产品,把产品放到仓库里,仓库里就有了产品,可以恢复销售了。

可能会有人问,为什么不让生产和销售直接挂钩呢,把仓库这个环节去掉?

这样会造成两种不好的情况:

一是突然来了很多单子,生产人员累成死Dog也生产不出来。

二是很长时间没有单子,生产人员闲成废Dog也无事可做。


用稍微“专业”点的术语就是此时的生产和销售是一种强耦合的关系,销售的波动对生产影响太大。

仓库就是一个缓冲区,能有效的吸收波动,很大程度上减少波动的传递,起到一种解耦作用,由强耦合变成一种松散耦合。

这其实就对应计算机里经典的生产者和消费者问题。


经典的生产者和消费者


一到多个线程充当生产者,生产元素。一到多个线程充当消费者,消费元素。

在两者之间插入一个队列(Queue)充当缓冲区,建立起生产者和消费者的松散耦合。

正常情况下,即生产元素的速度和消费元素的速度差不多时,生产者和消费者其实是不需要去关注对方的。

生产者可以一直生产,因为队列里总是有空间。消费者可以一直消费,因为队列里总是有元素。即达到一个动态的平衡。

但在特殊情况下,比如生产元素的速度很快,队列里没有了空间,此时生产者必须自我“ba工”,开始“睡大觉”。

一旦消费者消费了元素之后,队列里才会有空间,生产者才可以重启生产,所以,消费者在消费完元素后有义务去叫醒生产者复工。

更准确的说法应该是,只有在生产者“睡大觉”时,消费者消费完元素后才需要去叫醒生产者。否则,其实可以不用叫醒,因为人家本来就没睡。

反之,如果消费元素的速度很快,队列里没有了元素,只需把上述情况颠倒过来即可。

但这样的话就会引入一个新的问题,就是要能够准备的判断出对方有没有在睡大觉,为此就必须定义一个状态变量,在自己即将开始睡大觉时,自己设置下这个变量。

对方通过检测这个变量,来决定是否进行叫醒操作。当自己被叫醒后,首先要做的就是清除一下这个变量,表明我已经醒来复工了。

这样就需要多维护一个变量和多了一部分判断逻辑。可能有些人会觉得可以通过判断队列的“空”或“满”(即队列中的元素数目)来决定是否进行叫醒操作。

在高并发下,可能刚刚判断队列不为空,瞬间之后队列可能已经变为空的了,这样会导致逻辑出错。线程可能永远无法被叫醒。

因此,综合所有,生产者每生产一个元素后,都会通知消费者,“现在有元素的,你可以消费”。

同样,消费者每消费一个元素后,也会通知生产者,“现在有空间的,你可以生产”。

很明显,这些通知很多时候(即对方没有睡大觉时)是没有真正意义的,不过无所谓,只要忽略它们就行了。

就是“宁可错杀一千,也不放过一个”。首先要保证是正确的,然后才有资格去BB别的。


 
  public static void main(String[] args) {	
    Queue queue = new Queue();	
    new Thread(new Producer(queue)).start();	
    new Thread(new Producer(queue)).start();	
    new Thread(new Consumer(queue)).start();	
  }	
  	
  static class Producer implements Runnable {	
	
    Queue queue;	
    	
    Producer(Queue queue) {	
      this.queue = queue;	
    }	
    	
    @Override	
    public void run() {	
      try {	
        for (int i = 0; i < 10000; i++) {	
          doingLongTime();	
          queue.putEle(random(10000));	
        }	
      } catch (Exception e) {	
        e.printStackTrace();	
      }	
    }	
    	
  }	
	
  static class Consumer implements Runnable {	
	
    Queue queue;	
    	
    Consumer(Queue queue) {	
      this.queue = queue;	
    }	
    	
    @Override	
    public void run() {	
      try {	
        for (int i = 0; i < 10000; i++) {	
          doingLongTime();	
          queue.takeEle();	
        }	
      } catch (Exception e) {	
        e.printStackTrace();	
      }	
    }	
    	
  }	
	
  static class Queue {	
    Lock lock = new ReentrantLock();	
    Condition prodCond  = lock.newCondition();	
    Condition consCond = lock.newCondition();	
    	
    final int CAPACITY = 10;	
    Object[] container = new Object[CAPACITY];	
    int count = 0;	
    int putIndex = 0;	
    int takeIndex = 0;	
    	
    public void putEle(Object ele) throws InterruptedException {	
      try {	
        lock.lock();	
        while (count == CAPACITY) {	
          println("队列已满:%d,生产者开始睡大觉。。。", count);	
          prodCond.await();	
        }	
        container[putIndex] = ele;	
        println("生产元素:%d", ele);	
        putIndex++;	
        if (putIndex >= CAPACITY) {	
          putIndex = 0;	
        }	
        count++;	
        println("通知消费者去消费。。。");	
        consCond.signalAll();	
      } finally {	
        lock.unlock();	
      }	
    }	
    	
    public Object takeEle() throws InterruptedException {	
      try {	
        lock.lock();	
        while (count == 0) {	
          println("队列已空:%d,消费者开始睡大觉。。。", count);	
          consCond.await();	
        }	
        Object ele = container[takeIndex];	
        println("消费元素:%d", ele);	
        takeIndex++;	
        if (takeIndex >= CAPACITY) {	
          takeIndex = 0;	
        }	
        count--;	
        println("通知生产者去生产。。。");	
        prodCond.signalAll();	
        return ele;	
      } finally {	
        lock.unlock();	
      }	
    }	
  }


解说:其实就是对await/signalAll的应用,几乎面试必问。


源代码:

https://github.com/coding-new-talking/java-code-demo.git


推荐阅读:

640?wx_fmt=jpeg


200多篇 原创 技术文章 海量视频资源 精美脑图 面试题

长按扫码可关注获取 

欢迎关注640?wx_fmt=png点个再看640?wx_fmt=png

【QT开发教程】Qt中的跨平台文件系统操作 文件系统操作是应用程序开发中经常需要处理的任务。Qt提供了一套跨平台的文件系统操作API,使开发者能够在不同操作系统上无缝地进行文件和目录操作。本文将介绍Qt中的跨平台文件系统操作,包括基本概念、常用类和方法以及实际示例。 阅读详情

相关推荐

【记录】游戏体验报告怎么写(自我成长篇)

2019年3月更新: 这个倒是不用推翻,我个人虽然写的东西还是有一些偏差的,但是“表哥”说的倒是没毛病的,这篇主要是为了记录用,具体的干货也有,都在图片里,请自行提取 =====18年初原答案 作为一个游戏策划新人,游戏分析可能抓不到点子上,试着总结记录一下游戏体验报告分析的成长(因为水平不同,视野可能也不一样,也许之后几年回头看这些东西都是小儿科或者是错误的,所以只写出当下的想...

eat_human的博客 6万+

多线程的奥秘

一 多线程的实现方式一: package com.wjl.thread; public class ThreadTest { public static void main(String[] args) { //3.创建线程类的子类对象 MyThread thread01 = new MyThread(); //4.调用此对象的start()方法 thread01.start(); for (int i = 0

迦南学院 300

Unity游戏开发客户端Unity基础

Unity游戏开发客户端面经 之 Unity 篇

Sea3752的博客 2万+

如果把线程当作一个人来对待,所有问题都瞬间明白了

来源:公众号【编程新说】多线程的问题都曾经困扰过每个开发人员,今天将从全新视角来解说,希望读者都能明白。强烈建议去运行下文章中的示例代码,自己体会下。问题究竟出在哪里?一...

Java团长的博客 867

数字IC后端设计实战:从floorplan到powerplan的优化策略

本文深入探讨了数字IC后端设计中从Floorplan到Powerplan的核心优化策略。文章强调了Floorplan作为布局基础的重要性,并详细解析了Powerplan规划,特别是针对低功耗设计中MTCMOS的应用与IR Drop分析。通过实战经验分享,提供了从模块摆放到电源网络构建、再到IR Drop问题层层递进的解决方案,旨在帮助后端工程师构建稳健高效的芯片物理设计。

weixin_28338005的博客 353

CSI Tools 工具的安装 Intel 5300 + Ubuntu14.04

在 ubuntu14 系统上面安装 CSI Tools,2020最新教程,亲测可用,只要保证和我做法一致绝对可以安装好。

WS的小屋 1万+

Java多线程

java多线程的基础知识

h295928126的博客 624

遥感新手别纠结!实测ENVI 5.3、5.6、6.0三个免费版,教你混搭安装避坑

本文为遥感新手提供ENVI 5.3、5.6、6.0三个免费版的实测对比与混搭安装指南,帮助用户构建零成本高效工作流。详细解析各版本功能差异、共存安装技巧及常见问题解决方案,特别适合课程作业、毕业论文分析和遥感入门练习。

weixin_30819085的博客 479

游戏服务器应该为每个用户开辟一个线程还是一个进程,为什么?

参考回答: 游戏服务器应该为每个用户开辟一个进程。因为同一进程间的线程会相互影响, 一个线程死掉会影响其他线程,从而导致进程崩溃。因此为了保证不同用户之 间不会相互影响,应该为每个用户开辟一个进程 ...

WangY.H 2002

Nordic开发者必备:SEGGER Embedded Studio免费许可证申请与配置全攻略

本文为Nordic芯片开发者提供了SEGGER Embedded Studio(SES)集成开发环境的详细配置指南。内容涵盖免费许可证的合规申请流程、项目创建、Nordic SDK集成,以及编辑器优化、构建调试配置、RTT日志输出等核心功能的高效设置,旨在帮助开发者构建深度优化的专业开发环境,显著提升嵌入式开发效率。

cheese的博客 353

AM驱动架构—优质Mini-LED显示技术解决方案

(VR)和增强现实(AR)设备:MiniLED背光驱动方案适用于虚拟现实和增强现实设备中的显示技术,通过高亮度和高对比度的显示效果,创造更逼真的虚拟体验此外,MiniLED背光还能够实现更快的响应速度,减少图像残影,使得用户的动作在虚拟环境中更加流畅自然。以其出色的性能和广泛的应用前景备受关注是实现MiniLED显示效果的重要环节。1)更高的亮度和对比度:尺寸更小,可以在同样面积的显示屏上配置更多的LED,并且通过独立控制每一个MiniLED,可以实现更高的亮度和更细致的对比度,使得显示画面更加鲜明清晰。

isweekDN的博客 2764

多线程之间的通讯

由于线程A和线程B持有同一个MyObject类的对象object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,假如线程A先执行methodA方法,那么线程B需要等待线程A执行完了methodA()方法之后,它才能执行methodB()方法。当条件满足时,线程B调用 notify()通知线程A,所谓通知线程A,就是唤醒线程A,让它尝试获取锁,若获取成功,则进入可运行状态,否则进入阻塞状态。因为,线程B已经发了通知了,以后不再发通知了。=5),线程A调用wait() 放弃CPU,并进入等待状态。

cxbtdd的博客 1103

解锁RK3588 HDMI输出:分辨率与EDID的深度探索

本文详细探讨了瑞芯微RK3588处理器的HDMI输出分辨率配置与EDID解析技术。文章首先介绍了RK3588处理器的性能特点及其HDMI功能在多媒体和工业领域的应用价值,随后分别阐述了HDMI输出分辨率的硬件连接要求和软件配置方法,包括4K/8K等不同分辨率的配置实例。在EDID部分,深入解析了EDID的数据结构、交互机制,以及数据获取与修改的具体方法。最后总结了配置过程中常见问题的解决方案,并展望了RK3588在高分辨率显示领域的发展前景。全文为开发者提供了RK3588 HDMI显示配置的完整技术指南。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 593

「面试」如果把线程当作一个人来对待,所有问题都瞬间明白了,你懂吗

多线程的问题都曾经困扰过每个开发人员,今天将从全新视角来解说,希望读者都能明白。 强烈建议去运行下文章中的示例代码,自己体会下。 要是喜欢的点点关注,点点赞。 对Java技术,架构技术感兴趣的朋友,欢迎加QQ群728821520,一起学习,相互讨论。 问题究竟出在哪里? 一个线程执行,固然是安全的,但是有时太慢了,怎么办? 老祖宗告诉我们,“一方有难,八方支援”,那不就是多叫几个线程来帮忙嘛,好办...

java1856905的博客 696

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

pyqt stop停止线程_【面试】如果把线程当作一个人来对待,所有问题都瞬间明白了...

多线程的问题都曾经困扰过每个开发人员,今天将从全新视角来解说,希望读者都能明白。强烈建议去运行下文章中的示例代码,自己体会下。问题究竟出在哪里?一个线程执行,固然是安全的,但是有时太慢了,怎么办?老祖宗告诉我们,“一方有难,八方支援”,那不就是多叫几个线程来帮忙嘛,好办呀,多new几个不就行了,又不要钱。这样能管用吗?继续往下看。俗话说,“在家靠父母,出门靠朋友”。有了朋友的帮助,就会事半功倍。是...

weixin_39782832的博客 82

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

5个步骤,教你瞬间明白线程和线程安全

作者 |一个程序员的成长责编 | 胡巍巍记得今年3月份刚来杭州面试的时候,有一家公司的技术总监问了我这样一个问题:你来说说有哪些线程安全的类?我心里一想,这我早都背好了...

优快云资讯 9万+

教你瞬间明白线程和线程安全

当前线程的访问,不会对另一个访问同一个方法的线程造成任何的影响。可以发现,count的值并不是正确的结果,当线程A读取到count的值,但是还没有进行修改的时候,线程B已经进来了,然后线程B读取到的还是count为1的值,正因为如此所以我们的count值已经出现了偏差,那么这样的程序放在我们的代码中,是存在很多的隐患的。最常见的一种,就是我们A线程在进入方法后,拿到了count的值,刚把这个值读取出来,还没有改变count的值的时候,结果线程B也进来的,那么导致线程A和线程B拿到的count值是一样的。

2201_75857869的博客 215
上一篇: 通过淘宝,大白话讲解大数据各个方向的分工
下一篇: MySQL知识点总结
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值