Loading

相关面试题

1.进程和线程的区别

        进程就是一个程序从创建,运行,到消亡的过程

          一个进程可以创建多个线程,每个线程都有自己的程序计数器,本地方法栈,虚拟机栈

2.说一下线程的生命周期?线程有哪几种状态?

         java线程在运行的生命周期中的指定时刻只能处于下面集中不同状态的其中一种状态。

 线程有5大状态:创建,就绪,运行,堵塞,死亡。

         创建(new)就是,线程被创建,但是还没有调用start()方法

         就绪(runnable):调用了start()方法,但是没有获取到CPU的使用权

         运行(running):获取到了CPU的使用权,执行程序代码

         阻塞(Blocked)状态:阻塞状态是线程因为某种原因放弃了CPU使用权,停止运行。直到线程进入就绪状态,才有机会转到运行状态

         阻塞又可以分为:

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

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

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

         死亡状态(Dead):线程执行完了,或者因为异常退出了run方法,该线程结束生命周期。

3.为什么要使用多线程?

        线程间的切换和调度的成本远远小于进程。另外多核CPU时代意味着多个线程可以同时运行。

       多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力和性能

4.说一下你对线程安全理解?

        线程安全指的是,我们写的某个代码块,在多个线程下也能够正确运行,不会混乱。比如i++,i的初始值为0,执行这个代码块,如果是线程安全的话,第一个线程的值为1,第二个线程的值为2;如果线程不安全的话,两个线程的值都是1.

线程安全的实现方法:1.互斥同步 2.非阻塞同步 3.无同步方案

 

5.线程之间如何通信?

        线程的通信可以被定义为:线程通信就是当个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。

线程通信的方式

线程通信主要可以分为三种方式,分别为共享内存消息传递(也叫等待-通知)管道流。每种方式有不同的方法来实现。在Java中线程之间的通信方式总共有8种,分别是:volatile、synchronized、interrupt、wait、notify、notifyAll、join、管道输入/输出

共享内存:线程之间共享程序的公共状态,线程之间通过 读-写 内存中的公共状态来隐式通信。比如 volatile 保证内存的可见性。

消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。比如 wait/notify/notifyAll等待通知方式和join方式

多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。 就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。于是我们引出了等待唤醒机制:(wait()notify())。notifyAll的使用情景是存在多个生产者和消费者。

等待通知机制是基于wait和notify方法来实现的,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被通知或者被唤醒。

管道流管道流是是一种使用比较少的线程间通信方式,管道输入/输出流和普通文件输入/输出流或者网络输出/输出流不同之处在于,它主要用于线程之间的数据传输,传输的媒介为管道。比如 管道输入/输出

管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信。像消息传递机制,也就是说:通过管道,将一个线程中的消息发送给另一个。

 

6.你知道守护线程吗?

    线程分为用户线程和守护线程,用户线程就是普通线程,守护线程就是JVM的后台线程,比如垃圾回收栈就是一个守护线程,守护线程会在其他普通线程都停止运行之后自动关闭。我们可以通过设置thread.setDaemon(true)来把一个线程设置为守护线程。

7.wait()和sleep()的区别?

 

   两者都可以暂停线程的执行。sleep()方法属于Thread类中的,而wait()方法属于Object类中。

   sleep通常用于暂停执行,wait通常被用于线程间交互/通信,

   sleep()方法导致了程序暂停执行指定的时间,让出CPU该其他线程,但监控状态依旧保持。

   调用sleep()方法,线程不会释放锁,调用wait(),线程会放弃锁

   sleep()必须捕获异常,而wait,notify和notifyAll不需要捕获异常

8.知道synchronized原理吗?

   synchronized是java提供的原子性内置锁,并且成为监视器锁。使用了synchronized之后,会在编译之后的同步代码块加上monitorenter和monitorexit字节码指令。执行monitorenter指令时会获取锁,如果获取到了锁,此时其他竞争锁的线程则会进入等待队列中,执行monitorexit指令将锁释放,处于等待队列中的线程再继续竞争锁。

   synchronized是排他锁,当一个线程获得锁以后,其他线程必须等待该线程释放锁以后才能获得锁。

9.volatile

  首先volatile是关键字,一旦一个共享变量被volatile修饰以后,那么就具备了两层语义:1.保证了不同线程对这个变量进行操作时的可见性(即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的)2.禁止进行指令重排序,紧接着volatile无法保证原子性,但是能禁止指令重排序,能保证有序性,可见性。

10.volatile怎么保证可见性和禁止指令重排序的?

         观察加入volatile关键字时所生成的汇编代码发现,会多出一个lock前缀指令。

                  lock前缀指令实际上相当于一个内存屏障,内存屏障会提供3个功能:

      1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

  2)它会强制将对缓存的修改操作立即写入主存;

  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

synchronized和volatile的区别?

       

  • Synchronized 是关键字,用来加锁。 Volatile 只是保持变量的线程可见性。通常适用于一个线程写,多个线程读的场景。
  • volatile 本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的。
  • volatile 仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
  • volatile 不会造成线程的阻塞;**synchronized **可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

 

11.lock

       Lock时接口,Lock需要手动加锁和手动解锁,lock.lock()方式加锁,lock.unlock()方法解锁。

       与Lock关联密切的锁有ReentrantLock和ReadWriteLock

                ReentrantLock实现了Lock接口,时可重入锁,内部定义了公平锁与非公平锁

                ReadWriteLock 一个用来过去读锁,一个用来获取写锁。把文件的读写操作分开,分成2个锁来分配给线程,从而使多个线程可以同时进行读操作,没有实现Lock接口。

synchronized和lock的区别

  1. synchronized是Java内置的一个关键字,Lock是是一个Java接口
  2. synchronized无法判断获取锁的状态,而lock锁可以判断是否获取到了锁
  3. synchronized回自动释放锁,而lock必须手动释放锁。如果不释放就会变成死锁
  4. synchronized 线程1(获得锁,阻塞)线程2(傻傻地等待),lock就不一定会等待
  5. synchronized 可重入锁,不可以中断的,非公平。lock锁 可重入锁,可以判断锁,公平不公平自己可以设置
  6. synchronized适合锁少量的代码同步问题 Lock适合锁大量的同步代码

 

说说ThreadLocal原理?

          ThreadLocal可以理解为线程本地变量,它会在每个线程都会创建一个副本,那么在线程之间访问内部副本变量就行了,做到了线程之间互相隔离。

 

 

 

锁的优化机制了解吗?

博客为啥引入偏向锁,轻量级锁。

 

posted @ 2022-03-19 20:21  远乡人  阅读(30)  评论(0编辑  收藏  举报