三个基础概念

  1. 原子性。一个操作或者一系列骚操作,要么全部执行要么全部不执行。(数据库中的“事物”就是个典型的原子操作)

  2. 可见性。当一个线程修改了共享属性的值,其它线程能立刻看到共享属性值的更改。(举个例子:由于JMM(Java Memory Model)分为主存和工作内存,共享属性的修改过程为从主存中读取并复制到工作内存中,在工作内存中修改完成之后,再刷新主存中的值。如果线程A在工作内存中修改完成但还没有刷新主存中的值,线程B看到的值还是旧值。这样可见性就没法保证)

  3. 有序性。程序的运行顺序似乎和我们编写逻辑的顺序是一致的,但计算机在实际执行中却并不一定。为了提高性能,编译器和处理器都会对代码进行重新排序。但是有个前提,重新排序的结果要和单线程执行程序顺序一致。

Java中控制并发的几种方式

  • volatile (用来保证可见性和有序性,不保证原子性)
  • synchronized
  • CAS/AQS
  • concurrent并发包

synchronized保证原子性、可见性和有序性。用来修饰方法或者代码块

  1. 根据锁对象的不同,一把锁同时最多只能被一个线程持有。

  2. 如果目标锁已经被当前线程持有,其它线程只能阻塞等待其它线程释放目标锁。

  3. 如果当前线程已经持有了目标锁,其他线程仍然可以调用目标类中没有被synchronized修饰的方法。

synchronized实现的是阻塞型并发,synchronized修饰的范围越大,瓶颈越高。为了解决这种问题,由此又有减小锁范围、减小锁粒度和锁分段之说。

synchronized锁住的代码块,同一时刻只能由一个线程访问。属于悲观锁。相对于这种需要挂起线程的悲观锁,还一种由CAS实现的乐观锁

CAS相对于synchronize,本质上也是一种阻塞的实现。只是阻塞的粒度(CPU指令级别)更小。

JDK在java/util/concurrent提供了很多常用的并发类及并发容器类。并发类基本是通过lock(CAS/AQS)实现,并发容器基本是通过synchronize和lock(CAS/AQS)实现的

各种锁来一波

  1. 独占锁:同一时刻只有一个线程持有同一锁,其余线程在链表中排队。
  2. 共享锁:同一时刻可以多个线程持有同一锁。
  3. 公平锁:锁被线程持有后,其余线程排队执行。锁按照FIFO放入链表。
  4. 非公平锁:锁被线程持有后,其余线程排队执行。锁按照FIFO放入链表。但是在刚释放锁的之后,如果有新线程竞争锁,那么新线程将和链表中下个即将被唤醒的线程竞争锁。

Java 线程面试题 Top 50

results matching ""

    No results matching ""