阅读 233

浅谈「synchronized」与「ReentrantLock」

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

synchronized与ReentrantLock

  • 首先去根上回答的话就是
    • 因为synchronized是有JDK提供的锁
      • 底层涉及到指令集的层面,如:
      1. 同步代码块是利用monitorentermonitorexit指令实现的

      2. 同步方法则是利用 flags 实现的。

图片.png

  • RenntranLock是java5提供的锁实现
    • java.util.concurrent包下提供的一套互斥锁

    • 但是和synchronized相比,其具有很多synchronized不能实现的功能

    • 比如最明显的例子就是RenntranLock可以实现「公平性」的自定义

图片.png

线程安全的概念:保证在多线程环境下的共享、可修改的状态是正确的

  • 来看下面代码

public class test {
    public int state;
    public void testSafe() {
        while (state < 100000) {
            int i = state++;
            int j = state;

            if (i != j - 1) {
                System.out.println("i is: " + i + "; j is: " + j);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        test t = new test();
        Thread ta = new Thread() {
            @Override
            public void run() {
                t.testSafe();
            }
        };
        Thread tb = new Thread() {
            @Override
            public void run() {
                t.testSafe();
            }
        };
        ta.start();
        tb.start();
        ta.join();
        tb.join();
    }
}
复制代码
  • 下图为代码的输出
    • 很明显看到出现了并发的线程修改了state的值

图片.png

  • 针对以上代码如果加上synchronized锁🔐的话,就不会有值的改变情况
synchronized (this){
    while (state < 100000) {
        int i = state++;
        int j = state;

        if (i != j - 1) {
            System.out.println("i is: " + i + "; j is: " + j);
        }
    }
}
复制代码
  • 上面代码我们使用的是synchronized(class){}锁住的是代码块

    • 而使用RenntranLock的话是代表一个线程试图获取一个已经获取锁的时候
      • 那么这个动作就是表示自动成功了
      • 公平性是指更偏向于把锁🔐赋予给等待时间最久的线程
      • 上部操作是为的是减少线程的饥饿
  • ReentrantLock相比synchronized,可以像普通对象一样使用

    • 可以利用其提供的各种便利方法,进行精细的同步操作,甚至是实现synchronized难以表达的用例,如:
      • 带超时的获取锁尝试。

      • 可以判断是否有线程,或者某个特定线程,在排队等待获取锁。

      • 可以响应中断请求。

    与重入锁相关的队列

public ArrayBlockingQueue(int capacity, boolean fair) {
   if (capacity <= 0)
       throw new IllegalArgumentException();
   this.items = new Object[capacity];
   lock = new ReentrantLock(fair);
   notEmpty = lock.newCondition();
   notFull =  lock.newCondition();
}
复制代码
  • ArrayBlockingQueue源码中我们可以看到
    • 两个条件变量是从同一个ReentrantLock创建出来的
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}
复制代码
  • 当队列为空的时候
    • take的线程调用await()执行等待入队
    • 不是直接返回,这就是形成了BlockingQueue的意思
    • 这一步是重点在notEmpty身上

入队操作

private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    notEmpty.signal();
}
复制代码
  • 入队操作住傲视通过signal()完成通知线程
    • 所以通过signal()await()的组合就完成了状态的闭环
文章分类
后端
文章标签