synchronized及JUC显式locks 使用原理解析

作者:Applehope 时间:2023-08-05 03:28:41 

一、synchronized 有不足

新事物的出现要不是替代老事物,要么就是对老事物的补充

JUC 的 locks 就是对 synchronized 的补充

  • 从开始竞争锁 到拿到锁这段时间,调用线程一直是阻塞状态,啥都干不了

  • 已经占有的资源也释放不了,别的线程也无法获得;这种不可抢占的情况,更容易带来死锁(死锁产生的原理就是:我占着你要用的资源不给你,你却不能抢)

  • 只有一个条件变量(条件等待队列),用于线程间的协调、通信

二、改进意见

采用更多的措施以避免死锁 :

不能抢占 变为 可抢占,占用部分资源的线程进一步申请其他资源时

  • 如果能快速申请到,就申请

  • 如果不能快速申请到,可以主动释放它占有的资源

使用者自己可创建多个条件变量,用于线程间的协调、通信。

三、可抢占的方 *

3.1 能够响应中断

synchronized 的问题是:持有锁 A 后,如果尝试获取锁 B 失败,那么线程就进入阻塞状态,一旦发生死锁,就没有任何机会来唤醒阻塞的线程。但如果阻塞状态的线程能够响应中断信号,也就是说当我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁 A。这样就破坏了不可抢占条件了。

3.2 支持超时

如果线程在一段时间之内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。

3.3 非阻塞地获取锁

如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。

四、可抢占的实现 - JUC 的 locks

// 支持中断的API
void lockInterruptibly() throws InterruptedException;
// 支持超时的API
boolean tryLock(long time, TimeUnit unit)  throws InterruptedException;
// 支持非阻塞获取锁的API
boolean tryLock();

五、多个 Condition(条件变量或条件等待队列)

等效于管程中的条件变量,条件变量用于线程间的同步。通过java.util.concurrent.locks.Lock#newCondition()来创建,每个 Condition 都具有一个 waitSet;这样锁对象就具备了多个waitSet; Condition 提供了以下方法:用于线程的协作(线程等待/激活)。

//线程加入此条件变量的等待队列;类似Object.wait();使用时也要放到while循环体内。
java.util.concurrent.locks.Condition#await()
java.util.concurrent.locks.Condition#awaitUninterruptibly()
java.util.concurrent.locks.Condition#awaitNanos()
java.util.concurrent.locks.Condition#await(long, java.util.concurrent.TimeUnit)
java.util.concurrent.locks.Condition#awaitUntil()
//激活此条件变量中的一个线程;类似Object.notify()
java.util.concurrent.locks.Condition#signal()
//激活此条件变量中的所有线程;类似Object.notifyAll()
java.util.concurrent.locks.Condition#signalAll()

java doc 示例:一个有界缓冲,两个条件等待队列,分别被生产者线程和消费者线程来使用。

  • 生产者线程在 notFull 条件队列中等待;意思为生产者线程要阻塞等待,直到 有界缓冲不是满的,才能 put

  • 消费者线程在 notEmpty 条件队列中等待;意思为消费者线程要阻塞等待,直到有界缓冲不是空的,才能 take

class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull  = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
  lock.lock();
  try {
    while (count == items.length)
      notFull.await();
    items[putptr] = x;
    if (++putptr == items.length) putptr = 0;
    ++count;
    notEmpty.signal();
  } finally {
    lock.unlock();
  }
}
public Object take() throws InterruptedException {
  lock.lock();
  try {
    while (count == 0)
      notEmpty.await();
    Object x = items[takeptr];
    if (++takeptr == items.length) takeptr = 0;
    --count;
    notFull.signal();
    return x;
  } finally {
    lock.unlock();
  }
}
}

六、JUC中locks的使用规范

6.1 保证锁的释放

Lock l = ...;
l.lock();
try {
  // access the resource protected by this lock
} finally {
  l.unlock();
}
Lock lock = ...;
if (lock.tryLock()) {
  try {
    // manipulate protected state
  } finally {
    lock.unlock();
  }
} else {
  // perform alternative actions
}

6.2 循环体中使用 await()

while (XXX)
   condition.await();

6.3 同步代码块中使用 await()

必须先持有锁

l.lock();
try {
   ...
   while (XXX)
     condition.await();
   ...
} finally {
  l.unlock();
}

来源:https://juejin.cn/post/7181027840932970554

标签:synchronized,JUC,显式,locks
0
投稿

猜你喜欢

  • spring的jdbctemplate的crud的基类dao

    2022-05-30 13:05:17
  • 解决@Autowired注入static接口的问题

    2022-01-02 03:16:29
  • maven环境变量配置讲解

    2023-11-29 12:35:51
  • java合并多个文件的实例代码

    2023-07-28 12:56:02
  • C# 10个常用特性汇总

    2023-03-22 01:04:13
  • HashMap在JDK7与JDK8中的实现过程解析

    2022-03-04 18:26:44
  • java对象初始化代码详解

    2023-09-19 13:59:01
  • C++异常处理 try,catch,throw,finally的用法

    2021-08-21 21:29:55
  • Springboot如何设置静态资源缓存一年

    2022-04-16 09:45:22
  • c#自定义Attribute获取接口实现示例代码

    2022-09-02 05:05:26
  • Unity3D运行报DllNotFoundException错误的解决方案

    2021-09-13 00:22:35
  • Java实现雪花算法(snowflake)

    2022-10-27 03:43:23
  • java文件输出流写文件的几种方法

    2023-11-08 16:17:30
  • C#给PDF文件添加水印

    2023-11-10 17:08:36
  • java实现多线程文件的断点续传

    2023-11-19 22:42:05
  • Android编程之绘图canvas基本用法示例

    2022-08-21 15:02:23
  • Spring boot实现文件上传功能

    2023-08-01 07:00:02
  • java日期格式化SimpleDateFormat的使用详解

    2023-08-25 03:22:15
  • Spring Cloud详解实现声明式微服务调用OpenFeign方法

    2021-07-04 07:56:41
  • 简单谈谈RxJava和多线程并发

    2023-08-02 00:27:52
  • asp之家 软件编程 m.aspxhome.com