Java并行程序基础,字节跳动+京东+美团+腾讯面试总结

1.  ![](https://www.icode9.com/i/ll/?i=20210721220143231.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0h1YmJlcnQwMQ==,size_16,color_FFFFFF,t_70)

2.  ![](https://www.icode9.com/i/ll/?i=20210721220226880.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0h1YmJlcnQwMQ==,size_16,color_FFFFFF,t_70)

3.  ![](https://www.icode9.com/i/ll/?i=20210721220409159.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0h1YmJlcnQwMQ==,size_16,color_FFFFFF,t_70)
  1. 线程的终止

    =====

    1. stop()方法是被废弃的方法,因为它会强行把执行到一半的线程终止,可能会引起一些数据的不一致问题。

      1. 比如说在处理用户记录的时候,有两条记录

        1. ID = 1, Name = 小明

        2. ID = 2, Name = 小王

      2. 如果用User对象进行保存,希望只保存要不记录1,要不记录2,。如果调用了stop()方法,就可能导致ID = 1,Name = 小王。这种现象出现。那么如果保存进行数据库,后果将不堪设想。

      3. stop()方法会直接终止线程,并且会立即释放这个线程所持有的的锁。而这些锁恰恰是用来维持对象一致性的,如果此时,写线程吸入数据正写到一半,并强行终止,那么对象就会被写坏,同时,由于锁已经被释放,另外一个等待该锁的读线程就顺理成章的读到这个不一致的对象。

    2. 一般终止可以通过设置一个boolean的标志,来控制线程是否继续执行下去。或者等待线程自动执行完。

  2. 线程的中断

    =====

    1. 线程中断是一种重要的线程协作机制。

    2. 中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出啦!至于目标线程接到通知后如何处理,则完全由目标线程自行决定。这点很重要,否则无条件的退出,跟stop()方法就没什么两样了。

    3. 与线程中断有以下三个方法

    4. 
      public void Thread.interrupt()              //中断线程
      
      public void Thread.isInterrupted()          //判断是否被中断
      
      public static boolean Thread.interrupted()  //判断是否被中断,并清除当前中断状态
      
      
    5. interrupt()方法是一个实例方法。它通知目标线程中断,也就是设置中断标志位。中断标志位表示当前线程已经被中断了。

    6. **isInterrupted()**方法也是实例方法,它判断当前线程是否有被中断(通过检查中断标志位)

    7. 静态方法interrupted()也是用来判断当前线程的中断状态,但同时会清除当前线程的中断标志位状态

    8. 
      public static void main(String[] args) throws InterruptedException {
      
              Thread t1 = new Thread(()->{
      
                 int i = 1;
      
                 while (true) {
      
                     if (Thread.currentThread().isInterrupted()) {
      
                         System.out.println("Interrupt!");
      
                         break;
      
                     }
      
                     System.out.println("I'm working " + i++);
      
                     Thread.yield();
      
                 }
      
              });
      
      
      
              t1.start();
      
              Thread.sleep(2000);
      
              t1.interrupt();
      
          }
      
      
    9. 这看起来跟前面增加标志位的手法非常相似,但是中断的功能更为强劲。比如,如果在循环体中,出现了类似于wait()或者sleep()这样的操作,则只能通过中断来识别了。

  3. 线程的睡眠sleep

    ==========

    1. 
      public static native void sleep(long millis) throws InterruptedException;
      
      
    2. 该方法会让当前线程休眠若干时间,会抛出InterruptedException中断异常。InterruptedException不是运行时异常,也就是程序必须捕获并且处理它,当线程在sleep休眠时,如果被中断,这个异常就会发生。

    3. 
      public static void main(String[] args) throws InterruptedException {
      
              Thread t1 = new Thread(()->{
      
                 while (true) {
      
                     if (Thread.currentThread().isInterrupted()) {
      
                         System.out.println("Interrupt!");
      
                         break;
      
                     }
      
      
      
                     try {
      
                         Thread.sleep(2000);
      
                     } catch (InterruptedException e) {
      
                         System.out.println("Interrupted When Sleep");
      
                         Thread.currentThread().interrupt();
      
                     }
      
                     Thread.yield();
      
                 }
      
              });
      
      
      
              t1.start();
      
              Thread.sleep(1000);
      
              t1.interrupt();
      
          }
      
      
    4. 如果在sleep的时候,线程被中断,则程序会抛出异常,并进入异常处理。在catch字句里,由于已经捕获了中断,我们可以立即退出线程,但是并没有这么做。因为**也许在这段代码中,还必须进行后续的处理,保障数据的一致性和完整性。**因此,执行了interrupt()方法再次中断自己,置上中断标志位。只有这么做,在检查isInterrupted(),才能发现当前线程已经被中断了。可以试一下将catch的interrupt注释掉进行验证。

    5. Thread.sleep()方法由于中断而抛出异常,此时,它会清除中断标记,如果不加处理,那么在下一次循环开始时,就无法不会这个中断,所以在异常处理中,再次设置中断标志位。

  4. 等待(wait)和通知(notify)

    ===================

    1. wait和notify不是在Thread类中的方法,而是在Object类中,意味着任何对象都能调用这两个方法。

    2. 如果一个线程调用了wait()方法,那么它就会计入object对象的等待队列。这个等待队列中,可能会有多个线程,因为系统运行多个线程同时等待同一个对象。当notify()被调用是,它就会从这个等待队列中,随机选择一个线程,并将其唤醒。但是这个选择不是公平的,并不是先等待的线程会优先被选择,这个选择完全是随机的。

    3. notifyAll()方法会唤醒这个等待队列的所有线程。

    4. 无论是wait()或者是notify()方法,必须包含在对应的synchronized语句中,无论是wait()或者notify()都需要首先获取目标对象的一个监视器。

    5. 而wait()方法执行后,会释放这个监视器,当被重新notify()后,**要做的第一件事不是继续执行后续的代码,而是要尝试重新获取object的监视器。**如果暂时无法获得,线程还必须要等待这个监视器。当监视器顺利获得后,才可以真正意义上的继续执行。

    6. wait()方法和sleep()的区别就是,wait会释放对象的锁,而sleep不会释放锁。

  5. 挂起(Suspend)和继续执行(resume)

    ========================

    1. 被挂起的线程必须要等到resume操作后,才能继续指定、

    2. 但是已经被标注为废弃方法,不推荐使用。因为suspend()在导致线程暂停的同时,并不会去释放任何资源。此时,任何线程想要访问被它暂用的锁,都会备受牵连,导致无法正常运行。直到对应的线程上进行了resume()操作,被挂起的线程才能继续操作。但是如果resume操作在suspend之前就执行了,那么被挂起的线程就很难有机会被继续执行了。

    3. 如果想要实现suspend跟resume,可以通过wait跟notify进行使用。

  6. 等待线程结束(join)和谦让(yield)

    ==========================

    1. 一个线程的输入可能非常依赖于另外一个或者多个线程的输出,所以,这个线程就需要等待依赖线程执行完毕,才能继续执行。

    2. 
      public final void join() throws InterruptedException
      
      public final synchronized void join(long millis) throws InterruptedException 
      
      
    3. 第一个join()方法表示无限等待,它会一直阻塞当前线程,知道目标线程执行完毕。

    4. 第二个join()给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及”,而继续往下执行。

    5. join就是加入的意思,因此一个线程要加入另外一个线程,那么最好的方法就是等着它一起走

    6. 
      public class JoinMain {
      
          public volatile  static int i = 0;
      
          public static class AddThread extends Thread {
      
              @Override
      
              public void run() {
      
                  for (i = 0; i < 1000000; i++);
      
              }
      
          }
      
      
      
          public static void main(String[] args) throws InterruptedException {
      
              AddThread at = new AddThread();
      
              at.start();
      
              at.join();
      
              System.out.println(i);
      
          }
      
      }
      
      
    7. 主函数中,如果不用join()等待AddThread,那么得到的i很可能是0或者一个非常小的数字。因为AddThread还没执行完,i的值就已经被输出了。但使用join方法后,表示主线程愿意等待AddThread执行完毕,跟着AddThread一起往前走,所以在join()返回,AddThread已经执行完成,故i总是1000000;

    8. join的本质是让调用线程wait()在当前线程对象实例上。

    9. 
      if (millis == 0) {
      
         while (isAlive()) {
      
            wait(0);
      
         }
      
      } 
      
      
    10. 可以看到,它调用线程在当前线程对象上进行等待。当执行完成后,被等待的线程会在退出前**调用notifyAll()**通知所有的等待线程继续执行。

Java网盘:pan.baidu.com/s/1MtPP4d9Xy3qb7zrF4N8Qpg
提取码:2p8n

总结:心得体会

既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。

学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](

)**

大厂Java架构核心笔记(适合中高级程序员阅读):

业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](

)**

大厂Java架构核心笔记(适合中高级程序员阅读):

Java并行程序基础,字节跳动+京东+美团+腾讯面试总结

上一篇:[转]nio学习-多路复用-epoll源码分析


下一篇:防抖和节流