Java多线程(一)

  1. 进程和线程的区别?

    • 进程是一个‘执行中的程序’,是系统进行资源分配和调度的一个独立单位
    • 线程是进程的一个实体,一个进程一般拥有多个线程。线程之间共享地址空间和其他资源(所以通讯和同步等操作,线程比进程更容易)
    • 线程一般不拥有系统资源,但是也有一些必不可少的资源(使用ThreadLocal存储)
    • 线程的上下文切换比进程的上下文切换要快得多<如何理解这句话?>
      • 进程切换时,涉及到当前进程CPU环境的保存和新被调度运行进程的CPU环境的设置
      • 线程切换时,仅需要保存和设置少量寄存器内容,不涉及存储管理方面的操作。
  2. 线程可以拥有属于自己独立的资源嘛?

    答:可以的,通过ThreadLocal可以存储线程的特有对象,也就是属于当前线程的资源。

  3. 进程之间常见的通讯方式?

    • 通过使用套接字Socket来实现不同机器间的进程通讯
    • 通过映射一段可以被多个进程访问的共享内存来进行通讯
    • 通过写进程和读进程利用管道进行通讯
  4. 线程的状态有哪些?

    • NEW
    • RUNNABLE
    • BLOCKED
    • WAITING
    • TIMED_WAITING
    • TERMINATED
  5. Notify和Notify All的区别?

    • notify All会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
    • notify只会随机选取一个处于等待池中的线程进入锁池中去竞争获取锁的机会
  6. Start和Run方法的区别?

    • 调用Start方法会创建一个新的子线程并启动
    • Run方法只是Thread的一个普通方法的调用
  7. Thread和Runnable的区别?

    • Thread是一个实现了Runnable接口的类,使得Run支持多线程
    • 因类的单一继承原则,推荐多使用Runnable接口
  8. Interrupt函数?

    调用interrupt()通知线程应该中断了.

    1. 如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常
    2. 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,被设置中断标志的线程将继续正常那个执行,不受影响。
  9. sleep和wait的区别?

    sleep方法是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态,当睡眠时间到了,会解除阻塞,进入可运行状态,等待CPU的到来。睡眠不释放锁

    wait方法是Object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。但是,置于重新占用互斥锁之后才能进入可运行状态。睡眠时,会释放互斥锁。

  10. join和yield方法?

join方法:当前线程调用,则其他线程全部停止,等待当前线程执行完毕,接着执行。

yield方法:该方法是的线程放弃当前分到的CPU时间,但是不是线程阻塞,即线程扔处于可执行状态,随时可能再次分的CPU时间。

  1. 线程死锁的问题:死锁是最常见的一种线程活性故障。死锁的起因是多个线程之间相互等待对方而被永远暂停(处于非Runnable)。死锁产生的四个必要条件。

    1. 资源互斥:一个资源每次只能被一个线程使用
    2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
    3. 不剥夺条件:线程已经获得的资源,在未使用完之前,不能强行剥夺。
    4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
  2. 如何避免死锁的发生?

    1. 粗锁法:使用一个粗粒度的锁来消除“请求与保持条件”,缺点是会明显降低程序的并发性能会导致资源的浪费
    2. 锁指定法:(必须回答出来的点)指定获取锁的顺序,比如某个线程只能获得A锁和B锁,才能对某个资源进行操作,在多线程条件下,如何避免死锁? - ->通过指定锁的获取顺序,比如规定,只有获得A锁的线程才有资格获得B锁,按顺序获取锁就可以避免死锁。这通常被认为是解决死锁很好的一种方式。
    3. 使用显式锁中的ReentrantLock.try(Long,Timeunit)来申请锁。
  3. 谈一谈对Synchronized关键字的理解

答:synchronized是Java的一个关键字,是一个内部锁。他可以使用在方法上和方法块上。表示同步方法和同步方法块。在多线程环境下,同步方法或者同步代码块在同一时刻只允许有一个线程在执行,其余的线程都在等待获取锁,也就是实现了整体并发中的局部串行。

  1. 谈一谈内部锁底层的实现?

    • 进入时,执行monitorEnter,将计数器+1,释放锁monitorExit时,计数器-1
    • 当一个线程判断到计数器为0时,则当前锁空闲,可以占用。反之,当前线程进入等待状态。
  2. 谈一谈你对Volatile关键字的理解?

    答:volatile 关键字可以用来修饰实例变量和类变量。被 volatile 修饰后,该变量或获得以下特性:

    1. 可见性。任何线程对其修改,其它线程马上就能读到最新值;

    2. 有序性。禁止指令重排序。

      volatile可以保证主内存和工作内存直接产生交互,进行读写操作,保证可见性
      volatile技能保证变量写操作的原子性,不可以保证读操作的原子性
      volatile可以禁止指令重排序(通过插入内存屏障),典型的案例是在单例模式中使用

  3. ReentrantLock和synchronized的区别?

    答:ReentrantLock是显示锁,其提供了一些内部锁不具备的特性,但并不是内部锁的替代品。显式锁支持公平和非公平的调度方式,默认采用非公平调度。

    synchronized 内部锁简单,但是不灵活。显示锁支持在一个方法内申请锁,并且在另一个方法里释放锁。显示锁定义了一个tryLock()方法,尝试去获取锁,成功返回true,失败并不会导致其执行的线程被暂停而是直接返回false,即可以避免死锁

  4. ThreadLocal的了解?

    ThreadLocal变量的作用域是为线程,也就是说线程内跨方法共享。例如某个对象的方法A对threadLocal变量赋值,在同一个线程中的另外一个对象的方法B能够读取到该值。因为作用域为同一个线程,那么自然就是线程安全的。

    ThreadLocal内部实现原理:

    • 每个线程内部都会维护一个类似HashMap的对象,称为ThreadLocalMap,里边会包含若干了Entry(K-V键值对),相应的线程被称为这些Entry的属主线程
    • Entry的Key是一个ThreadLocal实例,Value是一个线程特有对象。Entry的作用是为其属主线程建立起一个ThreadLocal实例与一个线程特有对象之间的对应关系
    • Entry对Key的引用是弱引用;Entry对Value的引用是强引用。
  5. 谈一谈对CAS算法的理解?

    答:CAS是Compare and swap的缩写,翻译过来就是比较替换。其实CAS是乐观锁的一种实现。而Synchronized则是悲观锁。这这里的乐观和悲观指的是当前线程对是否有并发的判断。CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

    ​ 悲观锁——认为每一次自己的操作大概率会有其他线程在并发,所以自己在操作前都有对资源进行锁定,这种锁定是排他的。悲观锁的缺点是不但把多线程并行转成了串行,而且枷锁和释放锁都有额外的开支。

    ​ 乐观锁——认为每一次操作时大概率不会有其他线程并发,所以操作时并不枷锁,而是对数据操作时比较数据的版本,和自己更新前取得的版本一致才进行更新。乐观锁省掉了加锁、释放锁的资源消耗,而且在并发量并不是很大的时候,很少会发生版本不一致的情况,此时乐观锁效率会更高。

    ​ Atomic变量的操作是如何保证原子性的,其实就是使用了CAS算法。

    ​ Atomic 变量在做原子性操作时,会从内存中取得要被更新的变量值,并且和你期望的值进行比较,期望的值则是你要更新操作的值。如果两个值相等,那么说明没有其它线程对其更新,本线程可以继续执行。如果不等,说明有线程已经先于此线程进行了更新操作。那么则继续取得该变量的最新值,重复之前的逻辑,直至操作成功。这保证了每个线程对 Atomic 变量操作是线程安全的

  6. CAS的缺点?

    1. 循环时间长开销很大;

      • 如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
    2. 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,可以使用循环CAS的方式保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这时候就可以用锁来保证原子性。

    3. ABA问题。如果内存地址V初次读取的值是A,并且在准备赋值的时候检查到它的值仍然为A,那我们就能说它的值没有被其他线程改变过了吗?

      如果在这段期间它的值曾经被改成了B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA”问题。Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。

  7. JVM对资源进行调度方式分为公平调度和非公平调度?

    公平调度方式:按照申请的先后顺序授权资源的独占权

    非公平调度方式:新来的线程(活跃线程)可以先被授予该资源的独占权。

  8. 什么是OOM?

    OOM,全称是Out of Memory,意思就是当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就抛出这个error

    为什么会OOM?

    1. 分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的JVM参数指定)太少
    2. 应用用的太多,并且用完了没释放,浪费了。此刻就造成内存泄漏或者内存溢出。

    最常见的OOM情况:

    1. Java.lang.OutOfMemoryError:Java Heap space --> java堆内存溢出,此种情况最常见。
  9. .lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出

原文地址:https://www.cnblogs.com/shine-rainbow/p/12343646.html