2019独角兽企业重金招聘Python工程师标准>>>
问题:
count++,当多线程执行时,是否是原子执行?答案是否定的。
当然,加上sychronized后就是原子操作了,但是只是实现这么简单的功能至于用这么重的锁么。。。
一、CAS(Compare And Swap):
Compare And Swap就是比较并且交换的一个原子操作,由cpu在指令级别上进行保证。
CAS 操作包含三个操作数:1、内存中的地址V,2、期望的值A,3、要修改的值B。
如果说V上的变量的值时A的话,就用B重新赋值,如果不是A,那就什么事也不做,操作的返回结果原值是多少。
循环CAS:在一个(死)循环【for(;;)】里不断进行CAS操作,直到成功为止(自旋操作)。
来段代码:
package com.btx.service;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;public class CounterTest {private AtomicInteger atomicInteger = new AtomicInteger();private Integer i = 0;static class My implements Runnable{private CountDownLatch countDownLatch;private CounterTest counterTest;public My(CountDownLatch countDownLatch, CounterTest counterTest){this.countDownLatch = countDownLatch;this.counterTest = counterTest;}public void run(){countDownLatch.countDown();for(int i=0; i<100;i++){counterTest.count();counterTest.casCount();}}}private void count(){this.i++;}private void casCount(){// this.atomicInteger.incrementAndGet();for(;;){int i = this.atomicInteger.get();boolean f = this.atomicInteger.compareAndSet(i,++i);if(f)break;}}public static void main(String[] args) throws InterruptedException {CounterTest counterTest = new CounterTest();CountDownLatch countDownLatch = new CountDownLatch(100);for(int i=0; i<100; i++){new Thread(new My(countDownLatch,counterTest)).start();}countDownLatch.await();System.out.println("非原子操作:"+ counterTest.i);System.out.println("原子操作:"+ counterTest.atomicInteger.get());}
}
解释下:
启用100个线程,每个线程累加一100次。我们期望的结果值应该是100*100=10000。
结果:
结论证明:
count++确实不是原子操作,并且CAS确实可以实现原子性。
CAS实现原子操作的三个问题:
1、ABA问题:
时间点1 :线程1查询值是否为A
时间点2 :线程2查询值是否为A
时间点3 :线程2比较并更新值为B
时间点4 :线程2查询值是否为B
时间点5 :线程2比较并更新值为A
时间点6 :线程1比较并更新值为C
线程2是原子的,但是线程2对变量的修改对线程1来说是透明的。但是线程1却将变量修改成功了,所以线程2就不是原子操作了。
2、循环时间很长的话,cpu的负荷比较大
3、对一个变量进行操作可以,同时操作多个共享变量有点麻烦
CAS的线程安全:
通过硬件层面的阻塞实现原子操作的安全
原子更新基本类型类
AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
AtomicInteger的常用方法:
- int addAndGet(int delta):
- boolean compareAndSet(int expect,int update):
- int getAndIncrement(): 原子递增,但是返回的是自增以前的值
- incrementAndGet原子递增,但是返回的是自增以后的值
- int getAndSet(int newValue)
原子更新数组类
AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下:
- int addAndGet(int i,int delta):
- boolean compareAndSet(int i,int expect,int update):
数组通过构造方法传入,类会将数组复制一份,原数组不会发生变化
原子更新引用类型提供的类
- AtomicReference: 可以解决更新多个变量的问题
- AtomicStampedReference:解决ABA问题
- AtomicMarkableReference:解决ABA问题
原子更新字段类
Atomic包提供了以下3个类进行原子字段更新。
- AtomicReferenceFieldUpdater:
- AtomicIntegerFieldUpdater:
- AtomicLongFieldUpdater:
二、Lock
有了synchronized为什么还要Lock?
1、 尝试非阻塞地获取锁
2、 获取锁的过程可以被中断
3、 超时获取锁
Lock的标准用法
public static void main(String[] args) {Lock lock = new ReentrantLock();lock.lock();try{// do my work.....}finally{lock.unlock();}}
Lock的常用API
- lock()
- lockInterruptibly:可在获取锁的时候中断
- tryLock:尝试非阻塞地获取锁,如果没有获取锁,就立即返回false
- unlock()
可重入锁
递归的时候发生锁的重入,如果不是可重入锁,就会死锁
公平锁和非公平锁
公平锁,先对锁发出获取请求的一定先被满足。公平锁的效率比非公平锁效率要低。
Lock接口的实现:
ReentrantLock、ReentrantReadWriteLock
Condition接口有何用处?
condition接口和Lock配合来实现等待通知机制
Condition接口的使用规范:
public class ConditionTemplete {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();public void waitc() throws InterruptedException {lock.lock();try{condition.await();}finally{lock.unlock();}}public void waitnotify() throws InterruptedException {lock.lock();try{condition.signal();//condition.signalAll();尽量少使用}finally{lock.unlock();}}}
ReentrantLock:
独占锁,只能有一个线程获取锁
Talk is cheap. Show me the code
结合ReentrantLock和Condition实现线程安全的有界队列
public class BlockingQueueLC<T> {private List queue = new LinkedList<>();private final int limit;Lock lock = new ReentrantLock();private Condition needNotEmpty = lock.newCondition();private Condition needNotFull = lock.newCondition();public BlockingQueueLC(int limit) {this.limit = limit;}public void enqueue(T item) throws InterruptedException {lock.lock();try{while(this.queue.size()==this.limit){needNotFull.await();}this.queue.add(item);needNotEmpty.signal();}finally{lock.unlock();}}public T dequeue() throws InterruptedException {lock.lock();try{while(this.queue.size()==0){needNotEmpty.await();}T result = (T) this.queue.remove(0);needNotFull.signal();return result;}finally{lock.unlock();}}
}
ReentrantReadWriteLock:
允许多个读线程同时进行,但是只允许一个写线程(不允许其他读线程和写线程),支持读多写少场景,性能会有提升。