首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ReentrantLock允许线程多次进入对资源的锁定

ReentrantLock允许线程多次进入对资源的锁定
EN

Stack Overflow用户
提问于 2019-01-08 16:22:31
回答 2查看 724关注 0票数 1

ReentrantLock允许线程多次进入对资源锁定,

这在执行/效率/功能方面有什么好处?

请参阅此链接,https://www.geeksforgeeks.org/reentrant-lock-java/

我不明白使用内锁的意义,因为一旦外锁被任何线程获取,就不会有其他线程进入外锁之后的部分(直到时间锁被这个线程持有为止),而且可以肯定的是,外锁之后/之后的部分一次只会被一个线程执行,那么内锁的意义是什么,这意味着多次进入锁的意义是什么?

代码:

代码语言:javascript
复制
import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.ReentrantLock; 

class worker implements Runnable 
{ 
  String name; 
  ReentrantLock re; 
  public worker(ReentrantLock rl, String n) 
  { 
    re = rl; 
    name = n; 
  } 
  public void run() 
  { 
    boolean done = false; 
    while (!done) 
    { 
      //Getting Outer Lock 
      boolean ans = re.tryLock(); 

      // Returns True if lock is free 
      if(ans) 
      { 
        try
        { 
          Date d = new Date(); 
          SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
          System.out.println("task name - "+ name 
                     + " outer lock acquired at "
                     + ft.format(d) 
                     + " Doing outer work"); 
          Thread.sleep(1500); 

          // Getting Inner Lock 
          re.lock(); 
          try
          { 
            d = new Date(); 
            ft = new SimpleDateFormat("hh:mm:ss"); 
            System.out.println("task name - "+ name 
                       + " inner lock acquired at "
                       + ft.format(d) 
                       + " Doing inner work"); 
            System.out.println("Lock Hold Count - "+ re.getHoldCount()); 
            Thread.sleep(1500); 
          } 
          catch(InterruptedException e) 
          { 
            e.printStackTrace(); 
          } 
          finally
          { 
            //Inner lock release 
            System.out.println("task name - " + name + 
                       " releasing inner lock"); 

            re.unlock(); 
          } 
          System.out.println("Lock Hold Count - " + re.getHoldCount()); 
          System.out.println("task name - " + name + " work done"); 

          done = true; 
        } 
        catch(InterruptedException e) 
        { 
          e.printStackTrace(); 
        } 
        finally
        { 
          //Outer lock release 
          System.out.println("task name - " + name + 
                     " releasing outer lock"); 

          re.unlock(); 
          System.out.println("Lock Hold Count - " + 
                       re.getHoldCount()); 
        } 
      } 
      else
      { 
        System.out.println("task name - " + name + 
                      " waiting for lock"); 
        try
        { 
          Thread.sleep(1000); 
        } 
        catch(InterruptedException e) 
        { 
          e.printStackTrace(); 
        } 
      } 
    } 
  } 
} 

public class test 
{ 
  static final int MAX_T = 2; 
  public static void main(String[] args) 
  { 
    ReentrantLock rel = new ReentrantLock(); 
    ExecutorService pool = Executors.newFixedThreadPool(MAX_T); 
    Runnable w1 = new worker(rel, "Job1"); 
    Runnable w2 = new worker(rel, "Job2"); 
    Runnable w3 = new worker(rel, "Job3"); 
    Runnable w4 = new worker(rel, "Job4"); 
    pool.execute(w1); 
    pool.execute(w2); 
    pool.execute(w3); 
    pool.execute(w4); 
    pool.shutdown(); 
  } 
}
EN

回答 2

Stack Overflow用户

发布于 2019-01-08 16:43:33

我认为解释它是如何工作的是多余的,我们应该只使用lock()或tryLock()方法中的一个:

如果您的线程有多个任务要执行,其中一些任务独立于锁,那么您应该使用tryLock()。如果线程需要执行的所有任务都依赖于锁,那么应该使用lock()。

也就是说,当线程拥有或能够获得独立于锁获取的额外工作时,您应该使用tryLock()而不是lock()。

-可选:

假设您有四个任务,1到3由具有两个工作线程A和B的线程池执行。任务1和2共享一个资源,每次必须由一个线程访问,以防止损坏。

现在,如果你只是在没有试用的情况下锁定,你可能会遇到以下情况:

  1. 线程A启动任务1;
  2. 线程A获取资源锁;
  3. 线程B启动任务2;
  4. 线程B尝试获取锁,但被阻塞(sleeps);
  5. Thread A完成任务1,释放锁;again;
  6. Thread <
  7. 线程B唤醒,但线程A尚未切换;
  8. 线程A启动任务3;
  9. 线程B尝试获取锁,并完成任务2;
  10. 线程A完成任务3。

请注意,lock()挂起线程,直到锁被释放,因此线程B在线程A释放锁之前是完全无用的。线程B可以启动任务3,而不是等待锁,并在此期间完成它。

使用try-lock的算法可以像这样执行:

  1. 线程A启动任务1;
  2. 线程A测试并获取锁;
  3. 线程B启动任务2;
  4. 线程B测试锁,跳过任务2;
  5. 线程B启动任务3;
  6. 线程A完成任务1,释放锁;
  7. 线程A启动任务2;
  8. 线程B完成任务3;
  9. 没有其他任务,所以线程B sleeps;
  10. Thread A完成任务2,释放锁。

请注意,tryLock()不挂起调用线程,因此可以跳过阻塞任务,而线程B执行非阻塞任务。如果任务1和2很长,并且还有其他几个短的非阻塞任务,则它们都可以在任务1完成或任务2开始之前完成。

当然,实现线程池和任务管理比普通的锁定要复杂一些:任务可能必须挂起并返回到池中;当任何锁被释放时,休眠的空闲线程应该被唤醒。

如果您有许多非阻塞任务(或者至少不是在同一个锁上阻塞)以及一些阻塞任务,那么这是值得的,但是如果所有任务都阻塞在同一资源上,那么从一开始就不值得实现多线程。

票数 0
EN

Stack Overflow用户

发布于 2019-01-08 17:15:23

假设在一个类中有两个方法m1和m2,这两个方法都已同步,并且m2正在调用m1。在这种情况下,如果线程a已经获得了m1上的锁,并且不允许再次获得锁,那么thresad将继续等待调用m2 (因为它已经拥有了锁)

更多详细信息:

https://stackoverflow.com/questions/18596080/java-concurrent-reentrantlock-why-we-want-to-acquire-the-same-lock-multiple-ti

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54087766

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档