面试准备之多线程(二)

171 阅读3分钟

并发是指同一个时间段内多个任务同时都在执行。并行是指单位时间内多个任务同时在执行。

在多线程编程实践中,线程的个数往往多于CPU的个数,所以一般都称为多线程并发编程。

Java内存模型规定,将所有的变量都存放在主内存中,当线程使用变量时,会把主内存里面的变量复制到自己的工作内存,线程读写变量时操作的是自己工作内存中的变量。

Java中的synchronized关键字

线程的执行代码在进入synchronized代码块前会自动获取内部锁,这时候其他线程访问同步代码块时会被阻塞挂起。拿到内部锁的线程会在正常退出同步代码块或者抛出异常后或者在同步块内调用了该内置锁资源的wait系列方法时释放该内置锁。

另外,由于Java中的线程是与操作系统的原生线程一一对应的,所以当阻塞一个线程时,需要从用户态切换到内核态执行阻塞操作。

Java中的volatile关键字

该关键字可以确保一个变量的更新对其他线程立即可见。当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存。

volatile虽然提供了可见性保证,但并不保证操作的原子性。

Java中的CAS操作

CAS即Compare and Swap,其是JDK提供的非阻塞原子性操作,它通过硬件保证了比较-更新操作的原子性。JDK里面的Unsafe类提供了一系列的compareAndSwap方法。

CAS有四个操作数,分别为:对象内存位置、对象中的变量的偏移量、变量预期值和新的值。

JDK中的AtomicStampedReferance类给每个变量的状态值都配置了一个时间戳,从而避免了ABA问题的产生。

锁的概述

乐观锁与悲观锁

公平锁与非公平锁

ReentrantLock提供了公平和非公平锁的实现。

公平锁:ReentrantLock pairLock = new ReentrantLock(true)

非公平锁:ReentrantLock UnpairLock = new ReentrantLock(false)

独占锁与共享锁

独占锁保证任何时候都只有一个线程能得到锁,ReentrantLock就是以独占方式实现的。共享锁则可以同时由多个线程持有,例如ReadWriteLock读写锁,它允许一个资源可以被多线程同时进行读操作。

可重入锁

synchronized内部是可重入锁。原理是在锁内部维护一个线程标识,用来标识该锁目前被哪个线程占用,然后关联一个计数器。一开始计数器值为0,说明该锁没有被任何线程占用。当获取该锁的线程再次获取锁时发现锁拥有者是自己,就会把计数器值加一,释放该锁减一。当计数器值为0时,锁里面的线程标示被重置为null,这时候被阻塞的线程会被唤醒来竞争获取该锁。

自旋锁

由于Java中的线程是与操作系统中的线程一一对应的,所以当一个线程在获取锁失败后,会被切换到内核状态而被挂起。当该线程获取到锁是又将其切换到内核状态而唤醒该线程。从用户状态切换到内核状态的开销是比较大的。

自旋锁则是,当前线程在获取锁时,如果发现锁已经被其他线程占有,它不会马上阻塞自己,在不放弃CPU使用权的情况下,多次尝试获取。如果尝试指定的次数后仍没有获取到锁则当前线程才会被阻塞挂起。