(五)java多线程之Lock类
发布日期:2025-03-30 23:22:04 浏览次数:8 分类:精选文章

本文共 4976 字,大约阅读时间需要 16 分钟。

Java 并发编程 - Lock 锁机制

locks

Lock 是一个用于提供更加灵活的锁体验的机制。它允许开发者根据需要定义锁的行为,这与传统的 monopoly lock 有所不同。传统锁机制通常会阻塞所有试图访问共享资源的线程,直到持有锁的线程释放它。而Lock则提供了更细粒度的控制,同时允许线程在不影响其他线程的情况下以非阻塞方式访问共享资源。

传统同步机制的局限性

在传统的 Java 同步机制中,每个对象都有一个 intrinsic lock( Monitor ),这个 lock 是解耦的,它提供了绝对的同步能力。然而,这种机制的缺点在于它的使用方式比较粗略。在多线程环境中,任何一段使用 synchronized 修饰的代码都会阻塞所有试图进入该代码块的线程。这使得在某些场景下,例如读写锁、共享资源等情况下,使用传统的同步机制会显得力不从心。

##_lock的优势

相比于传统的 synchronizeLock, Lock 提供了更加灵活的锁机制。它允许开发者定义自己的锁的行为,这意味着我们可以根据具体的需求来配置锁。例如,某些情况下我们可能需要一个可重入锁,而另一些情况下可能需要非阻塞的锁。

lock 的基本使用方式

在使用 lock 的时候,最基本的流程是:

  • 获取锁:通过调用 lock.lock() 方法。
  • 使用锁:在锁被获取后进行操作。
  • 释放锁:在操作完成后,必须在 finally 块中调用 lock.unlock()。
  • 这种方式保证了锁的使用是显式的,也使得即使出现异常情况,锁也能正确地被释放。传统的 synchronize 是隐式的,这意味着一旦出现异常,代码块所在的 monitor 会被自动释放。

    使用 lock 的示例

    假设我们需要设计一个售票系统,其中多个线程可以同时购买票,而每次购买操作需要独占性的锁保护。以下是一个可能的实现方案:

    import java.util.concurrent.Lock;import java.util.concurrent.ReentrantLock;public class Ticket {    private static final int DEFAULT_TICKET_COUNT = 1000;    private int count = DEFAULT_TICKET_COUNT;    private int boughtCount = 0;    Lock lock = new ReentrantLock();    public boolean buyTicket(int count) throws InterruptedException {        if (count > this.count) {            Thread.sleep(10);            return false;        } else {            this.count -= count;            Thread.sleep(1);            boughtCount += count;            return true;        }    }}class TicketRunnable implements Runnable {    private Ticket ticket;    private Lock lock;    public TicketRunnable(Ticket ticket, Lock lock) {        this.ticket = ticket;        this.lock = lock;    }    @Override    public void run() {        for (int i = 0; i < 5; i++) {            lock.lock();            try {                int count = (int) Math.random() * 10 + 1;                boolean success = ticket.buyTicket(count);                System.out.println(Thread.currentThread().getName() +                                   "打算买" + count + "张票,买票" +                                   (success ? "成功" : "失败") +                                   ",还剩下" + ticket.getCount() +                                   "张票,总共卖掉" + ticket.getBuyedCount() +                                   "张票,总票数" + (ticket.getCount() + ticket.getBuyedCount()));                if (!success) {                    break;                }            } catch (Exception e) {                e.printStackTrace();            } finally {                lock.unlock();                try {                    Thread.sleep(10);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}public class SyncThreadTest {    public static void main(String[] args) throws InterruptedException {        for (int i = 0; i < 20; i++) {            new Thread(new TicketRunnable(new Ticket(), lock)).start();        }    }}ReentrantLock lock = new ReentrantLock();

    tryLock 方法

    除了基本的 lock() 方法,Lock 还提供了 tryLock() 方法,这允许线程在一定的时间内试图获取锁。如果锁不可用,不会阻塞,而是返回一个布尔值。

    使用 tryLock() 的好处在于我们可以在不影响其他线程的情况下,按照一定的策略进行锁的获取。例如,可以设置一个最长等待时间,超过该时间后退出并进行其他操作。

    以下是一个使用 tryLock() 方法的例子:

    import java.util.concurrent.Lock;public class TicketRunnable implements Runnable {    private Ticket ticket;    private Lock lock;    public TicketRunnable(Ticket ticket, Lock lock) {        this.ticket = ticket;        this.lock = lock;    }    @Override    public void run() {        for (int i = 0; i < 5; ) {            if (lock.tryLock()) {                try {                    int count = (int) Math.random() * 10 + 1;                    boolean success = ticket.buyTicket(count);                    System.out.println(Thread.currentThread().getName() +                                      "打算买" + count + "张票,买票" +                                      (success ? "成功" : "失败") +                                      ",还剩下" + ticket.getCount() +                                      "张票,总共卖掉" + ticket.getBuyedCount() +                                      "张票,总票数" + (ticket.getCount() + ticket.getBuyedCount()));                    if (!success) {                        break;                    }                } catch (Exception e) {                    e.printStackTrace();                } finally {                    lock.unlock();                    i++;                }            } else {                System.out.println(Thread.currentThread().getName() +                                   " 买票系统被占用,尝试获取锁失败.");                try {                    Thread.sleep(10);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}

    原理

    Lock 的核心思想是允许更多灵活性地控制线程对共享资源的访问。具体来说,Lock 提供了几种方法:

  • lock():阻塞获取锁,直到锁可用。
  • lockInterruptibly():与 lock() 相同,但保证能被打断。
  • tryLock():非阻塞获取锁,返回是否成功。
  • tryLock(long time, TimeUnit unit):尝试在指定时间内获取锁。
  • unlock():释放锁。
  • 通过这些方法,开发者可以灵活地控制线程对共享资源的访问,既可以采用阻塞方式,也可以采用非阻塞策略。

    总结

    在实际应用中,Lock 比传统的 synchronized 提供了更大的灵活性。它允许开发者根据特定需求定义锁的行为,适用于更复杂的多线程场景。通过合理使用锁,可以显著提升多线程程序的性能和安全性。

    如果觉得这篇文章很有用,欢迎在支付宝或微信打赏支持一下!

    上一篇:(从进程/线程视角看内存)鸿蒙内核源码分析
    下一篇:(zhuan) Evolution Strategies as a Scalable Alternative to Reinforcement Learning

    发表评论

    最新留言

    网站不错 人气很旺了 加油
    [***.192.178.218]2025年04月15日 21时12分10秒

    关于作者

        喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
    -- 愿君每日到此一游!

    推荐文章