1.原子操作类
包路径:java.util.concurrent.atomic
基础类其中包括:
布尔类型-AtomicBoolean,整形类型-AtomicInteger,浮点类型-AtomicLong,引用类型-AtomicReference。
其主要理念:由volatile修饰value保证可见性和有序性,使用unsafe进行CAS操作实现原子性操作。
其中主要方法 getAndSet() ,原子性的设置一个新值并返回旧值。在并发情况下,不需要synchronized就可以保证操作的原子性,开销相比于synchronized小很多,适合非阻塞算法实现并发控制。
升级版:
LongAdder和DoubleAdder类分别用户对Long和Double类型的累加器(只能操作对数值的加减),类中有base变量和数组cell,当并发竞争不严重时,直接对base通过CAS进行原子操作,当并发竞争严重时,将使用CAS操作cell数组,通过sum方法获取base+cell[]中所有值的和。相当于将一个值,拆分为多个值,通过增加线程竞争的目标来降低CAS操作的消耗。在高并发下,有更大的吞吐量,但会消耗更大的空间,也是一种空间换时间的做法。
LongAccumulator和DoubleAccumulator是LongAdder和DoubleAdder的升级版,可以自己定制操作,构造器需要传入一个函数式接口和初始值,当调用accumulate方法时,如果竞争不激烈,会直接通过函数式接口操作传入long或者double的值并赋给base,如果竞争激烈则直接将值cell数组中。通过get方法获得时会通过函数式接口计算base和cell数组中的所有值。
使用示例:
package com.ma.vue.boot.atomic;
import org.junit.Test;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
public class AtomicTest {
private static AtomicInteger atomicInteger= new AtomicInteger();//对于int的原子操作类
private static LongAdder longAdder = new LongAdder();//从0开始的对于long的原子累加器
private static LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x+y,0);//原始值为0 相加
private static LongAccumulator longAccumulator4multiplication = new LongAccumulator((x,y) -> x*y,1);//原始值为0 相乘
@Test
public void testAtomic(){
ExecutorService service = Executors.newCachedThreadPool();
for(int i = 0 ;i < 20 ; i++){
service.submit(() -> {
int randomInt = new Random().nextInt(100);
int andSet = atomicInteger.getAndSet(randomInt);
System.out.println("atomicInteger.getAndSet old value "+andSet+" new value " +randomInt);
//结果为10个20相加为200
longAdder.add(10);
System.out.println("longAdder value : "+longAdder.sum());
//结果为 20个2相加 为40
longAccumulator.accumulate(2);
System.out.println("longAccumulator accumulate value : " + longAccumulator.get());
//结果为 2的10次方1048576
longAccumulator4multiplication.accumulate(2);
System.out.println("longAccumulator4multiplication accumulate value : " + longAccumulator4multiplication.get());
});
}
service.shutdown();
}
}
2.锁
包路径:java.util.concurrent.locks
常用实现类包括ReentrantLock(可重入锁),ReentrantReadWriteLock(可重入读写锁),StampedLock(版本锁不可重入)。
这三个类都是基于CAS和QAS的实现,QAS是jdk提供的一个并发同步控制抽象类,即java.util.concurrent.locks.AbstractQueuedSynchronizer,提供了大量方法帮助我们实现自定义锁。
根据JDK中例子实现的自定义不可重入互斥锁:
package com.ma.vue.boot.lock;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 一个简单的QAS的实现,不可重入互斥锁
*/
public class AQSTest implements Lock, Serializable {
/**
* 借助AQS框架实现
*/
private static class Sync extends AbstractQueuedSynchronizer {
/**
* 返回是被处于被锁状态
*
* @return
*/
protected boolean isHeldExclusively() {
return getState() == 1;
}
/**
* 如果未加锁则尝试加锁
*
* @param acquires
* @return
*/
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 尝试释放锁
* @param releases
* @return
*/
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() { return new ConditionObject(); }
/**
* 为反序列化提供支持
* @param s
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0);//重置锁状态
}
}
private final AQSTest.Sync sync = new Sync();
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return new Sync().newCondition();
}
public boolean isLocked() { return sync.isHeldExclusively(); }
}
由于JDK对于synchronized的不断优化和升级,性能:synchronized=StampedLock>ReentrantReadWriteLock>ReentrantLock
示例:
ReentrantLock的简单使用:
package com.ma.vue.boot.lock;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
//可重入锁,必须手动释放锁
private final ReentrantLock reentrantLock = new ReentrantLock();
private final ExecutorService service = Executors.newCachedThreadPool();
private static int sum = 0;
//线程计数器
private static CountDownLatch latch = new CountDownLatch(10);
@Test
public void reentrantLockTest(){
for(int i=0;i<10;i++){
service.submit(() -> {
for(int j=0;j<10;j++){
reentrantLock.lock();
try{
sum ++;
reentrantLock.lock();
try{
sum ++;
}finally {
reentrantLock.unlock();
}
}finally {
reentrantLock.unlock();
}
}
//每执行完一个线程就将latch减一
latch.countDown();
});
}
try {
latch.await();//会等待至计数器为0被唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(sum);
}
}
ReentrantLock是可重入互斥锁的实现,可选择公平锁或非公平锁,非公平锁吞吐量较高,在较老版本JDK,效率比synchronized关键字高,消耗也相对较少。
ReentrantReadWriteLock的简单使用:
package com.ma.vue.boot.lock;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 用来实现并发的集合
* 当集合预期很大,读线程比写线程多 推荐使用
*/
public class ReadWriteLockDictionaryTest {
private final Map<String, Object> map = new TreeMap<String, Object>();
//读写锁
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Object getObject(String key) {
Object o = null;
r.lock();
try {
o = map.get(key);
} finally {
r.unlock();
}
return o;
}
public Object[] getAllKeys() {
r.lock();
try {
return map.keySet().toArray();
} finally {
r.unlock();
}
}
public Object put(String key, Object o) {
w.lock();
try {
return map.put(key, o);
} finally {
w.unlock();
}
}
public void clear() {
w.lock();
try {
map.clear();
} finally {
w.unlock();
}
}
}
ReentrantReadWriteLock是一个中可重入读写锁,读读共享,读读共享,写写互斥,读写互斥。
StampedLock的使用示例:
package com.ma.vue.boot.lock;
import java.util.concurrent.locks.StampedLock;
public class StampedLockTest {
private long sum;
//读写锁,读锁可由乐观锁升级为悲观锁,进一步提升性能,但不可重入
private final StampedLock sl = new StampedLock();
public void move(long sum){
long stamp = sl.writeLock();//返回一个版本号
try {
this.sum = sum;
}finally {
sl.unlockWrite(stamp);
}
}
/**
* 默认获取乐观锁增加效率,当版本变化时获取悲观锁保证数据的正确
* @return
*/
public long read(){
//获取一个乐观读锁的版本号
long stamp = sl.tryOptimisticRead();
//获取数据
long sum = this.sum;
//对比版本是否变化
if(!sl.validate(stamp)){
//版本变化时则获取悲观读锁
stamp = sl.readLock();
try{
//获取数据
sum = this.sum;
}finally {
sl.unlockRead(stamp);
}
}
return sum;
}
/**
* 默认使用悲观读锁,当无法升级为写锁时再显示获取写锁
* @param newSum
*/
public void updateIfAtOrigin(long newSum){
//获取读锁
long stamp = sl.readLock();
try {
while (sum == 0) {
//尝试根据版本号将读锁升级为写锁,成功返回版本号,失败返回0
long ws = sl.tryConvertToWriteLock(stamp);
if(ws != 0L){
//成功则写入值
stamp = ws;
this.sum = newSum;
break;
}else{
//失败则释放读锁,获取写锁
sl.unlockRead(stamp);
stamp = sl.readLock();
}
}
}finally {
sl.unlock(stamp);
}
}
}
StampedLock是一种可重入读写锁,并将读锁可由乐观锁升级为悲观锁,进一步提升了吞吐量,并且提供了一种悲观读锁升级为写锁的操作方式。
如果想让线程进行awite和notify操作,可以使用Condition对象进行操作,示例:
package com.ma.vue.boot.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 通过condition 实现一个有界缓冲区
*/
public class ConditionTest {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
final Object [] item = new Object[100];
//放置元素索引,消耗元素索引,计数器
int putInx =0 ,takeInx = 0 ,count= 0;
/**
* 新增元素,如果缓冲区满了,则进入等待状态
* @param object
* @throws InterruptedException
*/
public void put(Object object) throws InterruptedException {
lock.lock();
try{
while (count == item.length)
notFull.await();
item[putInx] = object;
if(++putInx == item.length)
putInx = 0;
++count;
notFull.signal();
}finally {
lock.unlock();
}
}
/**
* 消耗元素,如果缓冲区空了,则进入等待状态
* @return
* @throws InterruptedException
*/
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = item[takeInx];
if(++takeInx == item.length)
takeInx = 0;
--count;
notFull.signal();
return x;
}finally {
lock.unlock();
}
}
}
以上大部分示例都为JDK源码中的示例,仅做学习记录之用。