1. 什么是CAS
答:CAS全称是Compare And Swap,即比较替换,是实现并发应用到的一种技术。操作包含三个操作数—内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。
CAS存在三大问题:ABA问题,循环时间长开销大,以及只能保证一个共享变量的原子操作
2. CyclicBarrier和CountDownLatch区别
答:CountdownLatch适用于所有线程通过某一点后通知方法,而CyclicBarrier则适合让所有线程在同一点同时执行
CountdownLatch利用继承AQS的共享锁来进行线程的通知,利用CAS来进行–,而CyclicBarrier则利用ReentrantLock的Condition来阻塞和通知线程。
3. volatile关键字的作用
答:volatile 变量具备两种特性:一种是保证该变量对所有线程可见,在一个线程修改了变量的值后,新的值对于其他线程是可以立即获取的;
一种是 volatile 禁止指令重排,即 volatile 变量不会被缓存在寄存器中或者对其他处理器不可见的地方,因此在读取 volatile 类型的变量时总会返回最新写入的值。因为在访问 volatile 变量时不会执行加锁操作,也就不会执行线程阻塞,因此 volatile 变量是一种比 synchronized 关键字更轻量级的同步机制。volatile 主要适用于一个变量被多个线程共享,多个线程均可针对这个变量执行赋值或者读取的操作。
4. 如何正确的使用wait()?使用if还是while?
答:wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。所以,应该使用while,实现循环调用
5. 为什么wait、nofity和nofityAll这些方法不放在Thread类当中
答:JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
6. synchronized和ReentrantLock的区别
答:Synchronized 是 Java 内建的同步机制,所以也有人称其为 Intrinsic Locking,它提供了互斥的语义和可见性,当一个线程已经获取当前锁时,其他试图获取的线程只能等待或者阻塞。Java 5 之前,synchronized 是仅有的同步手段,在代码中,Synchronized 可以用来修饰方法,代码块。
ReentrantLock , 通常翻译为可重入锁,是 Java 5 提供的锁实现,通过代码直接调用 lock() 方法获取代码书写也更加灵活,与此同时,ReentrantLock 提供了很多实用的方法,可以实现很多 synchronized 无法做到的细节控制,但是需要明确调用 unlock()方法释放。
用法比较
Lock使用起来比较灵活,但是必须有释放锁的配合动作Lock必须手动获取与释放锁,而 synchronized不需要手动释放和开启锁Lock只适用于代码块锁,而 synchronized可用于修饰方法、代码块等
特性比较
ReentrantLock的优势体现在具备尝试非阻塞地获取锁的特性:当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁迮被中断地获取锁的特性:与synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛岀,同时锁会被释放超时获取锁的特性:在指定的时间范围内获取锁;如果截止时间到了仍然 无法获取锁,则返回.
注意事项
在使用 Reentrantlock类的时,一定要注意三点在 finally 中释放锁,目的是保证在获取锁之后,最终能够被释放不要将获取锁的过程写在try块內,因为如果在获取锁时发生了异常,异常拋岀的同时,也会导致锁无故被释放。 Reentrantlock提供了—个 newCondition的方法,以便用户在同一锁的情况下可以根据不同的情况执行等待或唤醒的动作。
7. 什么是AQS
答:AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。AQS是基于FIFO的队列实现的,并且内部维护了一个状态变量state,通过原子更新这个状态变量state即可以实现加锁解锁操作。
8. 什么是Java内存模型
答:java内存模型(JMM)是一套规范,在多线程中,一方面,要让编译器和CPU可以灵活地重排序;
另一方面,要对开发者做一些承诺,明确告知开发者不需要感知什么样的重排序,需要感知什么样的重排序。然后,根据需要决定这种重排序对程序是否有影响。如果有影响,就需要开发者显示地通过volatile、synchronized等线程同步机制来禁止重排序。
9. 什么是自旋
答:当一个线程拿不到锁的时候,可以不放弃CPU,空转,不断重试,也就是所谓的“自旋”。对于多CPU或者多核,“自旋”就很有用了,因为没有线程切换的开销。
AtomicInteger的实现就用的是“自旋”策略,如果拿不到锁,就会一直重试。
10. Java编程写一个会导致死锁的程序
代码如下:
```java
public class DeadLockDemo {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (lockA) {
System.out.println(name + " got lockA, want LockB");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB) {
System.out.println(name + " got lockB");
System.out.println(name + ": say Hello!");
}
}
}
}, "线程-A").start();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (lockB) {
System.out.println(name + " got lockB, want LockA");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockA) {
System.out.println(name + " got lockA");
System.out.println(name + ": say Hello!");
}
}
}
}, "线程-B").start();
}
}
运行结果如下:
线程-A got lockA, want LockB
线程-B got lockB, want LockA