Java并发面试题

1、线程的生命周期、线程有哪些状态

  线程通常有五种状态:创建、就绪、运行、阻塞、和死亡状态

  阻塞的情况分为三种:

  (1)、等待阻塞:运行的线程执行wait方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify或者notifyAll方法才能被唤醒,wait是Object类的方法

 (2)、同步阻塞:运行的线程在获取对象的同步锁时,若该锁被别的线程占用,则JVM会把该线程放入“锁池”中。

 (3)、其他阻塞:运行的线程执行sleep或join方法时,或者发出了I/O请求时,JVM会把该线程置为阻塞状态,当sleep状态超时、join等待线程终止或超时,或者I/O处理完毕,线程重新转入就绪状态,sleep是  Thread类的方法。

 

2、对线程安全的理解

 不是线程安全,应该是内存安全。堆是共享内存,可以被所有线程访问

    概念:当多个线程访问一个对象时,如果不能进行额外的同步控制或其他的协调操作,调用这个对象的行为都可以获得正确的结果,我们就说这个线程是安全的。

 

3、ThreadLocal的底层原理

  1.ThreadLocal是Java中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据
  2. ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值
  3.如果在线程池中使用ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使用完之后,应该要把设置的key ,value,也就是Enty对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Enty对象也就不会被回收,从而出现内存泄漏,解决办法是,在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清楚Entry对象

  4.ThreadLocal经典的应用场景就是连接管理(一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)

 

  

 

 

 

4、并发、并行、串行之间的区别

  1.串行:一个任务执行完,才能执行下一个任务

  2.并行(Parallelism):两个任务同时执行
  3. 并发(Concurency):两个任务整体看上去是同时执行,在底层,两个任务被拆成了很多份,然后一个一个执行,站在更高的角度看来两个任务是同时在执行的

 

5、并发的三大特性

  原子性:

    例如:i++

    先将i从主存读到工作区内存中的副本中,+1运算,再将结果写入工作内存中。--》需保证前三步完成后才能刷回主存    最后将工作内存的值刷回主存

  可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值

  有序性

 

6、Java死锁如何避免 

  造成死锁的几个原因:
    1.一个资源每次只能被一个线程使用
    2.一个线程在阻塞等待某个资源时,不释放已占有资源
    3.一个线程已经获得的资源,在未使用完之前,不能被强行剥夺

    4.若干线程形成头尾相接的循环等待资源关系这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满足其中某一个条件即可。而其中前3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。
  在开发过程中:
    1.要注意加锁顺序,保证每个线程按同样的顺序进行加锁

    2.要注意加锁时限,可以针对所设置一个超时时间
    3.要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决

 

 

 

  

7、为什么使用线程池,解释下线程池参数

  1、降低资源消耗:提高线程利用率,降低创建和销毁线程的消耗。

  2、提高响应速度:任务来了,直接有线程可用可执行,而不是先创建线程,再执行。

  3、提高线程的可管理性:线程是稀缺资源,使用线程池可以统一分配调优监控

  

8、线程池的底层工作原理

  线程池内部是通过队列+线程实现的,当我们利用线程池执行任务时:
    1.如果此时线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

    2.如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
    3.如果此时线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
    4.如果此时线程池中的线程数量大于corePoolSize,缓冲队列lworkQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
    5.当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数

 

9、线程池中线程复用原理

  线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过Thread创建线程时的一个线程必须对应一个任务的限制。
  在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread进行了封装,并不是每次执行任务都会调用Thread.start()来创建新线程,而是让每个线程去执行一个"循环任务",在这个"循环任务"中不停检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的run方法串联起来。

  

 

posted @ 2022-09-19 17:27  Homnay  阅读(39)  评论(0)    收藏  举报