zoukankan      html  css  js  c++  java
  • 并发编程面试题

    1.进程和线程还有协程之间的关系

    • 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
    • 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
    • 协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

    一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;

    资源分配给进程,同一进程的所有线程共享该进程的所有资源;

    处理机分给线程,即真正在处理机上运行的是线程;

    线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

    协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。

     一个线程就是执行一个子程序

    协程在子程序内部是可中断的,然后转而执行别的子程序,在适当的时候再返回来接着执行。

    2.并发和并行之间的区别

    并发:指统一时间内,宏观上处理多个任务

    并行:指统一时间内,真正上处理多个任务

    并发的关键是你有处理多个任务的能力,不一定要同时。

    并行的关键是你有同时处理多个任务的能力。

    3.Java中多线程实现的方式

    继承Thread类,实现Runable接口,但是除以上两种方法外,还可以通过使用ExecutorService、Callable、Future实现有返回结果的多线程

    4.Callable和Future模式

    在java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。

    然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。

    我们一般只能采用共享变量或共享存储以及线程通信的方式实现获得任务结果的目的;  

    不过,在java中,也提供了使用Callable和Future来实现获取任务结果的操作。

    Callable用来执行任务,产生结果,而Future用来获得结果;

    Future模式是多线程开发中非常常见的一种设计模式。

    它的核心思想是异步调用。当我们需要调用一个函数方法时。

    如果这个函数执行很慢,那么我们就要进行等待。

    但有时候,我们可能并不急着要结果。

    因此,我们可以让被调用者立即返回,让他在后台慢慢处理这个请求。

    对于调用者来说,则可以先处理一些其他任务,在真正需要数据的场合再去尝试获取需要的数据。


    5.线程池创建的方式(一般不适用Excecutors.nexxxx创建,一般使用ThreadPoolExecutor)

    • newFixedThreadPool(int nThreads)
      • 创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,
      • 这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程
    • newCachedThreadPool()
      • 创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,
      • 而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制
    • newSingleThreadExecutor()
      • 这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;
      • 它的特点是能确保依照任务在队列中的顺序来串行执行
    • newScheduledThreadPool(int corePoolSize)
      • 创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。


    6.Java当中线程状态有哪些

    线程状态有 5 种,新建,就绪,运行,阻塞,死亡。

    7.多线程中的常用方法

     start,run,sleep,wait,notify,notifyAll,join,isAlive,currentThread,interrupt,yield

    8.线程状态流程图

    9.volatile关键字有什么用途,和Synchronize有什么区别

    volatile关键字:解决的是内存可见性的问题,会使得所有对volatile变量的读写都会直接刷到主存,即保证了变量的可见性。这样就能满足一些对变量可见性有要求而对读取顺序没有要求的需求。

    volatile关键字和Synchronize的区别

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

    10.指令重排和先行发生原则

    重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。(多线程不安全,遵循as-if-serial语义)

    先行发生原则-它是判断数据是否存在竞争、线程是否安全的主要依据。

    11.并发编程线程安全三要素
    1.原子性:原子性说明一个或者多个操作要么都成功,要么都失败,中间不能中断,也不存在上下文切换的问题

    原子类可以避免操作数值的原子性问题,在java.util.concurrent.atomic下的原子类

    如何保证线程运行的原子性:

    解决办法:

    • 可以使用Synchronize和Lock将多不操作变为原子操作,但是volatile不能保证原子性
    • 可重入锁

    2.可见性:
    保证多线程下,所有线程对数据操作其他线程是可见的,可以使用volatile和sync

    3.有序性:
    程序执行得顺序按照代码的先后顺序执行,JVM可能会对指令进行重排,不改变结果的前提下进行重排


    12.进程和线程间调度算法:

    • 先来先服务调度算法
    • 短作业优先调度算法
    • 高响应调度法
    • 时间片轮训算法
    • 优先级调度算法


    13.Java开发中用过哪些锁:

    悲观锁/乐观锁

    公平锁/非公平锁

    可重入锁/不可重入锁

    自旋锁

    共享锁/互斥锁

    死锁

    分段锁

    分布式中:

    分布式锁

    数据库中:

    行锁,表锁等


    14.Synchronize关键字理解

    Synchronize关键字是:非公平的,悲观的,独享的,可重入的一把线程同步的锁

    主要应用于解决并发控制的问题,主要有两种用法,分别是同步方法和同步代码块

    15.CAS无锁机制

    CAS:Compare and Swap,即比较再交换。

    CAS无锁机制:本身无锁,采用乐观锁的思想,在数据操作时对比数据是否一致,如果一致代表之前没有线程操作该数据,那么就会更新数据,如果不一致代表有县城更新则重试

    CAS当中包含三个参数CAS(V,E,N),V标识要更新的变量,E标识预期值,N标识新值


    16.AQS

    AQS:全成AbstractQueueSynchronizer,抽象队列同步器,这个类在java.util.concurrent.locks包下

    它是一个底层同步工具类,比如CountDownLatch,Sammphore,ReentrantLock,ReentrantReadWriteLock等等都是基于AQS

    底层三个内容:

    • 1.state(用于计数器)
    • 2.线程标记(哪一个线程加的锁)
    • 3.阻塞队列(用于存放阻塞线程)

    在AQS当中,假设当前线程A要获取锁,如果获取到锁资源则将state改为1,然后记录当前线程,在线程A没有释放掉锁的情况下,那么其他线程访问时,

    会产生阻塞,那么会将阻塞线程放入到队列(双向链表)当中,当线程A使用完该锁,调用了unLock时,那么在队列当中等待的线程就有执行得机会,如

    果成功获取到锁则出队,如果没有获取到锁则让下一个等待线程去获取

    17.ReentrantLock底层实现

    ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁

    18.ReentrantLock和Synchronize之间的区别

    功能区别:

    这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。

    而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成

    便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。

    锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized

    性能的区别:

    在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,

    但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,

    在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。

    都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。


    19.ReentrantReadWriteLock

    ReentrantReadWriteLock:Lock的实现类,其读锁是共享锁,其写锁是独享锁


    20.BlockingQueue阻塞队列的实现方式

    • ArrayBlockingQueue
      • ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。

      • 有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。

    • LinkedBlockingQueue
      • LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,

      • 它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。

      • 它的内部实现是一个链表。

    • PriorityBlockingQueue
      • PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样

      • PriorityBlockingQueue中允许插入null对象。

    • SynchronousQueue
      • SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。

    21.ConcurrentLinkedQueue

    ConcurrentLinkedQueue : 是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,

    通常ConcurrentLinkedQueue性能好于BlockingQueue.

    它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。

    头是最先加入的,尾是最近加入的,该队列不允许null元素。

    22.什么是死锁,怎么解决

    假设有P1和P2两个进程,都需要A和B两个资源,现在P1持有A等待B资源,而P2持有B等待A资源,

    两个都等待另一个资源而不肯释放资源,就这样无限等待中,这就形成死锁,这也是死锁的一种情况。

    给死锁下个定义,如果一组进程中每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的。

    • 竞争不可抢占资源引起死锁
    • 竞争可消耗资源引起死锁
    • 进程推进顺序不当引起死锁

    产生死锁的必要条件

    • 互斥条件
      • 某资源只能被一个进程使用,其他进程请求该资源时,只能等待,知道资源使用完毕后释放资源。
    • 请求和保持条件
      • 程序已经保持了至少一个资源,但是又提出了新要求,而这个资源被其他进程占用,自己占用资源却保持不放。
    • 不可抢占条件
      • 进程已获得的资源没有使用完,不能被抢占。
    • 循环等待条件
      • 必然存在一个循环链。

    解决死锁的思路

    • 预防死锁
      • 破坏死锁的四个必要条件中的一个或多个来预防死锁。
    • 避免死锁
      • 和预防死锁的区别就是,在资源动态分配过程中,用某种方式防止系统进入不安全的状态。
    • 检测死锁
      • 运行时出现死锁,能及时发现死锁,把程序解脱出来
    • 解除死锁
      • 发生死锁后,解脱进程,通常撤销进程,回收资源,再分配给正处于阻塞状态的进程。
  • 相关阅读:
    SQL SERVER 2000 配置文件 SETUP.INI
    (转)Sybase ASE基础知识:利用Sybase Central简单操作Sybase ASE数据库
    新软发布:Autorun病毒免疫工具
    vc 编程最需要注意的地方
    (转)不得不了解VB中的CallByName
    作业总结
    (转)傻瓜式简单制作Windows7旗舰版免激活光盘镜像教程 (安装后自动激活)
    发布C#模块:平面凸包的计算
    凸包计算模块ConvexHull的使用方法
    模块发布——树类模块
  • 原文地址:https://www.cnblogs.com/wishsaber/p/12584388.html
Copyright © 2011-2022 走看看