锁分为Synchronized和锁Lock。
ReentrantLock与锁
ReentrantLock(可重入锁)与Synchronized(同步锁)的区别:
- 可重入性:区别不大,都是通过计数器,当计数器下降为0时,锁释放。
- 锁的实现:Synchronized依赖JVM,ReentrantLock基于JDK实现。(类似Synchronized是操作系统实现,ReentrantLock是敲代码实现),ReentrantLock可通过看源码明白实现,Synchronized依赖JVM就看不到实现原理。
- 性能的区别:优化之前Synchronized比ReentrantLock性能差很多,优化后Synchronized引入偏向锁、轻量级锁也就是自旋锁后,2者性能差不多了。差不多时,官方推荐Synchronized。
- 功能的区别:Synchronized的使用方便简洁,隐式获取锁释放锁,ReentrantLock需要手动获取锁,释放锁,如果忘记容易出现死锁,所以最好再finally中释放锁。锁的颗粒度和灵活度很明显ReentrantLock优于Synchronized。 - ReentrantLock独有的功能: - ReentrantLock可指定公平锁、非公平锁,而Synchronized只能时非公平锁。公平锁:先等待的先获取锁,反之亦然。
- 提供了一个Condition类,可以分组唤醒需要唤醒的线程,Synchronized要么随机唤醒,要么全部唤醒。
- 提供能够中断等待锁的线程的机制,lock.lockInterruptibly(),lockInterruptibly是一种自旋锁,通过循环来调用CAS来加锁,性能比较好,就是因为不会进入内核阻塞状态。
 - 使用场景:必须实现上面3个功能时,就必须用了。 
代码示例一
ReentrantLock实现累加,注意与synchronized的不用用法
package com.yanxizhu.demo.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @description: ReentrantLock锁实现计数,注意使用synchroized怎么实现的
 * @author: <a href="mailto:batis@foxmail.com">清风</a>
 * @date: 2022/4/25 23:09
 * @version: 1.0
 */
@Slf4j
public class ReentrantLockDemo {
    //定义锁
    private static Lock lock = new ReentrantLock();
    //定义锁用户数
    private static final int totalThread = 5000;
    //定义并发线程数
    private static final int concurrentThread = 200;
    //定义总和
    private static int count = 0;
    public static void main(String[] args) throws Exception{
        //定义线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定义信号量
        final Semaphore semaphore = new Semaphore(concurrentThread);
        //定义闭锁
        final CountDownLatch countDownLatch = new CountDownLatch(totalThread);
        for (int i = 0; i < totalThread; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    sum();
                    semaphore.release();
                } catch (InterruptedException e) {
                    log.error("错误信息:【{}】", e.getMessage());
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("最后累加结果Count={}", count);
    }
    //通过ReentrantLock锁实现
    public static void sum() {
        lock.lock();
        count++;
        lock.unlock();
    }
    //累加求和(synchronized锁实现)
//    public synchronized static void sum() {
//        count ++;
//    }
    
}输出结果:
23:21:08.549 [main] INFO com.yanxizhu.demo.concurrency.lock.ReentrantLockDemo - 最后累加结果Count=5000源码查看
public ReentrantLock() {
        sync = new NonfairSync();
 }默认传入的是不公平的锁。
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }可以传入ture或false决定是否是公平锁。
需要注意的方法tryLok:
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }本质是仅在调用的时候,锁定未被另一个线程持有的情况下,才锁定。参数同样可以传入超时时间和单位,保持线程时间多久可以锁定。提供了丰富的方法,可以参考官方文档,

ReentrantReadWriteLock
没有任何读写锁的时候,才可以取得写入锁。可实现悲观读取。读多写少时,可能会导致写进入饥饿。注意是悲观锁,当有读时,写可能一直执行不了,有写的时候,不能读。
代码示例
package com.yanxizhu.demo.concurrency.lock;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @description: ReentrantReadWriteLock 读锁、写锁的使用
 * 注意: ReentrantReadWriteLock时悲观锁,读的时候不能写,写的时候不能读,互斥的
 * @author: <a href="mailto:batis@foxmail.com">清风</a>
 * @date: 2022/4/25 23:40
 * @version: 1.0
 */
public class ReentrantReadWriteLockDemo {
    //定义读写锁
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    //获取读锁
    private static final Lock readLock = lock.readLock();
    //获取写锁
    private static final Lock writeLock = lock.writeLock();
    private static Map<String, Data> map = new TreeMap<>();
    public class Data{
    }
    //读map中的数据
    public Data getValue(String key) {
        readLock.lock();
        try{
            return map.get(key);
        }catch (Exception e) {
        }finally {
            readLock.unlock();
        }
        return null;
    }
    //读取map中所有的key
    public Set<String> getKeys() {
        readLock.lock();
        try{
            return map.keySet();
        }catch (Exception e) {
        }finally {
            readLock.unlock();
        }
        return null;
    }
    //向map中写入数据
    public void writeDate(String key, Data value) {
        writeLock.lock();
        try{
            map.put(key, value);
        }catch (Exception e){
        }finally {
            writeLock.unlock();
        }
    }
}StampedLock
StampedLock控制锁,有3种模式。
写、读、
是由锁和版本控制,就是这里的。
读分为悲观读和乐观读。
官方代码示例
package com.yanxizhu.demo.concurrency.lock;
import java.util.concurrent.locks.StampedLock;
public class StampedLockDemo {
    class Point {
        private double x, y;
        private final StampedLock sl = new StampedLock();
        void move(double deltaX, double deltaY) { // an exclusively locked method
            long stamp = sl.writeLock();
            try {
                x += deltaX;
                y += deltaY;
            } finally {
                sl.unlockWrite(stamp);
            }
        }
        //下面看看乐观读锁案例
        double distanceFromOrigin() { // A read-only method
            long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
            double currentX = x, currentY = y;  //将两个字段读入本地局部变量
            if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
                stamp = sl.readLock();  //如果没有,我们再次获得一个读悲观锁
                try {
                    currentX = x; // 将两个字段读入本地局部变量
                    currentY = y; // 将两个字段读入本地局部变量
                } finally {
                    sl.unlockRead(stamp);
                }
            }
            return Math.sqrt(currentX * currentX + currentY * currentY);
        }
        //下面是悲观读锁案例
        void moveIfAtOrigin(double newX, double newY) { // upgrade
            // Could instead start with optimistic, not read mode
            long stamp = sl.readLock();
            try {
                while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
                    long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
                    if (ws != 0L) { //这是确认转为写锁是否成功
                        stamp = ws; //如果成功 替换票据
                        x = newX; //进行状态改变
                        y = newY;  //进行状态改变
                        break;
                    } else { //如果不能成功转换为写锁
                        sl.unlockRead(stamp);  //我们显式释放读锁
                        stamp = sl.writeLock();  //显式直接进行写锁 然后再通过循环再试
                    }
                }
            } finally {
                sl.unlock(stamp); //释放读锁或写锁
            }
        }
    }
}示例代码二
累加求和
package com.yanxizhu.demo.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.concurrent.ThreadSafe;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.StampedLock;
@Slf4j
@ThreadSafe
public class StampedLockDemo2 {
    // 请求总数
    public static int clientTotal = 5000;
    // 同时并发执行的线程数
    public static int threadTotal = 200;
    public static int count = 0;
    private final static StampedLock lock = new StampedLock();
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }
    private static void add() {
        //这里加锁,返回值
        long stamp = lock.writeLock();
        try {
            count++;
        } finally {
            //释放锁,带上加锁的返回值
            lock.unlock(stamp);
        }
    }
}输出结果:5000,线程安全,并发性好。
Condition
代码示例
package com.yanxizhu.demo.concurrency.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
 * ReentrantLock中另一个队列,Condition的使用
 */
@Slf4j
public class ConditionDemo {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition = reentrantLock.newCondition();
        new Thread(() -> {
            try {
                reentrantLock.lock();
                log.info("wait signal"); // 1
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("get signal"); // 4
            reentrantLock.unlock();
        }).start();
        new Thread(() -> {
            reentrantLock.lock();
            log.info("get lock"); // 2
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            condition.signalAll();
            log.info("send signal ~ "); // 3
            reentrantLock.unlock();
        }).start();
    }
} 
        
       
     
       
           
           
           
          
评论 (0)