如何在 Java 中使用锁java.util.concurrent.locks.Lock 教程和示例

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(364)   2023-06-26 07:54:18

我们已经知道关于 线程同步 以及使用同步 关键字的各种机制。 Java 提供了另一种基于 Lock 接口和实现它的类(例如 ReentrantLock).在本教程中,我们将看到 Lock 接口的基本用法,以解决打印机队列问题

锁定界面

java.util.concurrent.locks.Lock 是一种线程同步机制,就像同步块一样。但是,Lock 比同步块更灵活、更复杂。由于 Lock 是一个接口,您需要使用其实现之一才能在您的应用程序中使用 Lock。 ReentrantLock 就是 Lock 接口的一种实现。

下面是Lock接口的简单使用。


Lock lock = new ReentrantLock();

lock.lock();

//critical section

lock.unlock();

首先创建一个锁。然后它的 lock() 方法被调用。现在 Lock 实例被锁定。任何其他调用 lock() 的线程都将被阻塞,直到锁定锁的线程调用 unlock()。最后 unlock() 被调用,Lock 现在被解锁,其他线程可以锁定它。

Lock接口和synchronized关键字的区别

Lock 和 synchronized 块之间的主要区别是:

1) 尝试访问 synchronized 块时超时是不可能的。使用 Lock.tryLock(long timeout, TimeUnit timeUnit),是可以的。 2) synchronized 块必须完全包含在单个方法中。 Lock 可以在不同的方法中调用 lock()unlock()

使用锁模拟打印机队列

在此示例中,程序将模拟打印机的行为。您可以在不同的时间间隔内或同时向打印机提交多个打印作业。打印机将从打印机队列中取出一个作业并打印它。其余的工作将在那里等待轮到他们。一旦打印机完成手头的打印作业,它将从队列中选择另一个作业并开始打印。保持这种循环发生。

PrintingJob.java

此类代表可以提交给打印机的独立打印。这个类实现了Runnable接口,这样打印机就可以执行它了。


class PrintingJob implements Runnable
{
   private PrinterQueue printerQueue;

   public PrintingJob(PrinterQueue printerQueue)
   {
      this.printerQueue = printerQueue;
   }

   @Override
   public void run()
   {
      System.out.printf("%s: Going to print a document\n", Thread.currentThread().getName());
      printerQueue.printJob(new Object());
   }
}

打印机队列.java

此类代表打印机队列/打印机。打印机维护一个锁,以便在当前打印作业完成后立即开始新的打印作业。


class PrinterQueue
{
   private final Lock queueLock = new ReentrantLock();

   public void printJob(Object document)
   {
      queueLock.lock();
      try
      {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() + ": PrintQueue: Printing a Job during " + (duration / 1000) + " seconds :: Time - " + new Date());
         Thread.sleep(duration);
      } catch (InterruptedException e)
      {
         e.printStackTrace();
      } finally
      {
         System.out.printf("%s: The document has been printed\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
   }
}

让我们测试我们的打印机程序:


public class LockExample
{
   public static void main(String[] args)
   {
      PrinterQueue printerQueue = new PrinterQueue();
      Thread thread[] = new Thread[10];
      for (int i = 0; i < 10; i++)
      {
         thread[i] = new Thread(new PrintingJob(printerQueue), "Thread " + i);
      }
      for (int i = 0; i < 10; i++)
      {
         thread[i].start();
      }
   }
}

Output:

Thread 0: Going to print a document
Thread 9: Going to print a document
Thread 8: Going to print a document
Thread 7: Going to print a document
Thread 5: Going to print a document
Thread 6: Going to print a document
Thread 4: Going to print a document
Thread 3: Going to print a document
Thread 2: Going to print a document
Thread 1: Going to print a document
Thread 0: PrintQueue: Printing a Job during 8 seconds :: Time - Tue Jan 06 15:19:02 IST 2015
Thread 0: The document has been printed
Thread 9: PrintQueue: Printing a Job during 1 seconds :: Time - Tue Jan 06 15:19:11 IST 2015
Thread 9: The document has been printed
Thread 8: PrintQueue: Printing a Job during 8 seconds :: Time - Tue Jan 06 15:19:12 IST 2015
Thread 8: The document has been printed
Thread 7: PrintQueue: Printing a Job during 9 seconds :: Time - Tue Jan 06 15:19:21 IST 2015
Thread 7: The document has been printed
Thread 5: PrintQueue: Printing a Job during 7 seconds :: Time - Tue Jan 06 15:19:31 IST 2015
Thread 5: The document has been printed
Thread 6: PrintQueue: Printing a Job during 5 seconds :: Time - Tue Jan 06 15:19:39 IST 2015
Thread 6: The document has been printed
Thread 4: PrintQueue: Printing a Job during 2 seconds :: Time - Tue Jan 06 15:19:44 IST 2015
Thread 4: The document has been printed
Thread 3: PrintQueue: Printing a Job during 2 seconds :: Time - Tue Jan 06 15:19:46 IST 2015
Thread 3: The document has been printed
Thread 2: PrintQueue: Printing a Job during 5 seconds :: Time - Tue Jan 06 15:19:49 IST 2015
Thread 2: The document has been printed
Thread 1: PrintQueue: Printing a Job during 5 seconds :: Time - Tue Jan 06 15:19:54 IST 2015
Thread 1: The document has been printed

该示例的关键在于 PrinterQueue 类的 printJob() 方法。当我们想要使用锁来实现临界区并保证只有一个执行线程运行一段代码时,我们必须创建一个ReentrantLock对象。在临界区的开始,我们必须使用lock() 方法来获得锁的控制权。

在临界区结束时,我们必须使用 unlock() 方法来释放锁的控制权,并允许其他线程运行这个临界区。 如果在临界区末尾不调用unlock()方法,等待该块的其他线程将永远等待,导致死锁情况。 如果您在临界区使用 try-catch 块,请不要忘记将包含 unlock() 方法的语句放在 finally 区中。

阅读更多:如何在 java 中创建和解决死锁

您必须非常小心地使用 Locks 以避免死锁。当两个或多个线程被阻塞等待永远不会被解锁的锁时,就会发生这种情况。例如,一个线程(A)锁定一个Lock(X),一个线程(B)锁定一个Lock(Y)。如果现在,线程 (A) 尝试锁定 Lock (Y) 并且线程 (B) 同时尝试锁定 Lock (X),两个线程将被无限期阻塞,因为它们正在等待永远不会被释放的锁.请注意,出现问题是因为两个线程都试图以相反的顺序获取锁。

快乐学习!!

地址:https://www.cundage.com/article/how-to-use-locks-in-java-java-util-concurrent-locks-lock-tutorial-and-example.html

相关阅读

我们已经知道关于 线程同步 以及使用同步 关键字的各种机制。 Java 提供了另一种基于 Lock 接口和实现它的类(例如 ReentrantLock).在本教程中,我们将看到 Lock 接口的...
根据 java 文档,CountDownLatch 是一种同步辅助工具,它允许一个或多个线程等待,直到其他线程中执行的一组操作完成。 CountDownLatch 概念是 java concur...
在 JDK 8 之前,无法在 Java 中创建大型、线程安全的 ConcurrentHashSet。 java.util.concurrent 包甚至没有名为 ConcurrentHashSet...
如果您使用的是纯 Java,从版本 5 开始,我们有一个方便的调度程序类,允许以固定速率或固定延迟运行任务: import java.util.concurrent.Executors; imp...
在 Java 中,同步 代码块一次只能由一个线程执行。另外,java支持多线程并发执行。这可能会导致两个或多个线程同时访问相同的字段或对象。 同步是使执行中的所有并发线程保持同步的过程。同步避免...
我们的一位读者 Anant 问了这个非常好的问题,详细说明/列出了我们应该了解的关于多线程的所有相关主题,包括 Java 8 中所做的更改。(初级到高级)。他只想知道 Java 中多线程框架的演...
简单来说,空间就是运行的能力几个程序或一个程序的几个部分并行。并发使程序能够通过利用底层操作系统和机器硬件的未开发功能来实现高性能和吞吐量。例如,现代计算机有多个 CPU 或一个 CPU 中包含...
通过示例学习以编程方式在 Java 中创建死锁。此外,学习在源代码中检测死锁以及如何解决死锁情况。 一般情况下,如果程序同步不当,系统资源在线程之间共享,那么总是有可能出现死锁情况,即多个线程等...
Java HashMap 默认是不同步的。如果我们在多个线程正在添加和删除对的并发应用程序中从 HashMap 添加/删除键值对,我们最终可能会出现不一致的映射状态。学习同步 hashmap 和...
了解 Java 中 sleep() 和 wait() 方法的区别。了解何时使用哪种方法以及它们在 Java 并发 中带来的影响。 1. Java sleep() 和 wait() – 讨论 sl...