1.聊一聊多线程
一块共享空间或一个共享变量被多条线程操作。
多线程的程序要想让其正确运行,关键在于保证线程执行过程中的 可见性,原子性
常用的volatile就是保证线程间“可见性”的 同步关键字。
volatile如何保证可见性?他会让当前工作线程立即将缓存写到主存。其他处理器通过嗅探总线上传播过来的数据检测自己的缓存值是不是过期。如果过期其他处理器会重新从内存中把数据读取到缓存中处理。
但由于每一个线程执行都不是原子的,导致多个线程并发执行下,volatile会出现“写覆盖”的现象。
这时就要使用基于CAS原理的各种方法来保证原子操作了。例如JUC包中Atomic接口下的Integer实现类等等。
CAS意为“比较并交换”,工作内存每次把值写回到主存前,都会先判断:主存中的当前值是否为 工作内存执行操作前 从主存中拿的那个值。 只有当2个主存值相等,工作内存的值才会写回到主存。
CAS 为什么是原子性的操作呢?拿CAS原理的实现类举例:AtomicInteger类中调用了unsafe方法,这个unsafe方法存在于Rt.jar这个包下。 这里的unsafe方法实际上是调用的操作系统方法,操作系统直接操作内存偏移值来进行“写”操作,这一过程是原子性的。
那操作内存偏移值实际上做了什么呢?
最后到汇编语言这一层执行的是lock cmpxchgl操作。这条汇编指令执行时锁住了“北桥信号”,“北桥信号”负责与cpu联系并锁定内存,此时其他处理器没法访问同一片内存区域。
CAS缺陷:由于CAS操作只比较值,而不比较过程,因此存在ABA问题。即虽然开始和最后结果值相同,但中间过程经历的变动不影响CAS操作。解决方案就是 采用+“时间戳”验证每次修改的版本这种方法
2.哪些集合类是并发不安全的?哪些是并发安全的?
ArrayList,SetList,HashMap是并发不安全的。
copyOnWriteArrayList,copyOnWriteArraySet,ConcurrentHashMap 采用了volatile和cas操作是并发安全的
3.线程池
4.threadlocal
3.sychronized锁升级
无锁——无线程访问不加锁
偏向锁——如果只有一个线程访问一段同步代码,在这个线程获取锁的同事不会释放锁。即没有CAS操作。当又来了一个线程时,偏向锁会升级为轻量级锁
轻量级锁——当一个线程持有锁时,另一个线程回进入自旋状态。
自旋减少了线程频繁切换导致的性能损耗,但是增加了cpu占用时长。
当一个线程自旋超过一定次数或者又来了一个线程,轻量级锁会膨胀为重量级锁
重量级锁——重量级锁会使除了拥有锁的线程外,其他线程全部阻塞,无自旋
4.谈谈你对AQS的理解
AQS是一个基于FIFO的双向队列。通过CAS操作进行原子修改state的int值,维护线程同步状态。
ReentrantLock,CountDownLatch等都基于AQS来实现。AQS包含state变量,加锁线程,等待队列等
拿ReentrantLock举例。当一个线程1过来的时候,调用ReentrantLock的lock()方法,这个加锁的过程就是直接利用CAS操作将state变成1,并将当前线程设置为自己

线程2过来以后,发现state1,这个时候用CAS改变state为1的操作就会失败,然后线程2会判断一下当前线程是不是自己,不是自己则加锁失败,线程2进入等待队列。当线程1释放锁后,线程2就可以竞争锁了(非公平锁的情况),当然如果是公平锁的情况,当线程1释放锁后线程2自然就会得到锁
简单说一下线程1释放锁的过程,释放锁的过程就是将state变量减1,当减为0时彻底释放,并将加锁线程变量设置为null。(值得注意的是,可重入锁的情况下state的值并不一定是1,有可能是 2 3 4,这种情况下state直到减为0的时候才会释放锁)。

线程1彻底释放锁后,线程2重新尝试加锁,这时进行CAS操作将state由0变成1,并设置加锁线程变量。同时加锁成功后会出队列。

被折叠的 条评论
为什么被折叠?



