了解jdk源码-atomic和locks包

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源码中的示例,仅做学习记录之用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值