【问题标题】:Reetrant locks in javajava中的可重入锁
【发布时间】:2015-09-20 15:19:18
【问题描述】:

我是 Java 多线程的新手。我试图使用锁。这是我的代码示例。

package com;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class UsingLocks {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
    // TODO Auto-generated method stub


    UsingLocks job = new UsingLocks();
    Thread [] threads= new Thread[5];
    for(int i=0;i<5;i++){
        threads[i]= new Thread(new LockTask(job));
    }
    for(int i=0;i<5;i++){
        threads[i].start();
    }

}

public void lockingJob() {
    System.out.println("Thread "+Thread.currentThread().getName()+" trying to Acquire lock");
    try {
    lock.tryLock();
    //lock.lock(); //When I use this, code works fine
    int time=new Random().nextInt(10)+3;
    System.out.println("Thread "+Thread.currentThread().getName()+" Acquired lock for "+time+" seconds.");
    TimeUnit.SECONDS.sleep(time);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println("Now releasing lock "+Thread.currentThread().getName());
    lock.unlock();
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println("After Unlock "+Thread.currentThread().getName());



}




}
class LockTask implements Runnable{
UsingLocks job;
public LockTask(UsingLocks job) {
    // TODO Auto-generated constructor stub
    this.job=job;

}

@Override
public void run() {
    // TODO Auto-generated method stub
    job.lockingJob();


}

}

以下是我使用 tryLock() 时的输出

Thread Thread-1 trying to Acquire lock
Thread Thread-0 trying to Acquire lock
Thread Thread-2 trying to Acquire lock
Thread Thread-1 Acquired lock for 12 seconds.
Thread Thread-2 Acquired lock for 3 seconds.
Thread Thread-0 Acquired lock for 8 seconds.
Thread Thread-3 trying to Acquire lock
Thread Thread-3 Acquired lock for 9 seconds.
Thread Thread-4 trying to Acquire lock
Thread Thread-4 Acquired lock for 6 seconds.
Now releasing lock Thread-2
Exception in thread "Thread-2" java.lang.IllegalMonitorStateException
at      java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
at com.UsingLocks.lockingJob(UsingLocks.java:37)
at com.LockTask.run(UsingLocks.java:66)
at java.lang.Thread.run(Thread.java:745)
Now releasing lock Thread-4
Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
at com.UsingLocks.lockingJob(UsingLocks.java:37)
at com.LockTask.run(UsingLocks.java:66)
at java.lang.Thread.run(Thread.java:745)

现在,根据我的理解,当第一个线程执行 tryLock() 时,它应该获得锁,其他线程应该无法获得锁。但是如输出所示。在 Thread-1 获得锁后,线程-2 也获得了锁等等。这怎么可能。请告诉我我在这里缺少什么。 提前致谢。

【问题讨论】:

    标签: java multithreading locking reentrantlock


    【解决方案1】:

    原因是如果锁已经被另一个线程持有,tryLock 永远不会阻塞。

    下面是关于 tryLock() 的文档

    公共布尔tryLock()

    只有在调用时没有被另一个线程持有时才获取锁。

    如果锁没有被另一个线程持有,则获取锁,并立即返回值为 true,将锁持有计数设置为 1。即使此锁已设置为使用公平排序策略,如果该锁可用,则调用 tryLock() 将立即获取该锁,而不管其他线程当前是否正在等待该锁。这种“闯入”行为在某些情况下可能很有用,即使它破坏了公平性。如果您想尊重此锁的公平设置,请使用几乎等效的 tryLock(0, TimeUnit.SECONDS)(它也检测中断)。

    如果当前线程已经持有这个锁,那么持有计数加一并且方法返回true。

    如果锁被另一个线程持有,则此方法将立即返回值为 false。

    【讨论】:

      【解决方案2】:

      我稍微修改了你的代码并自己试了一下。

      我发现无论我做什么,ReentrantLock.tryLock 都会抛出 IllegalMonitorStateException。我觉得不合适。

      package concurrency;
      
      import java.util.ArrayList;
      import java.util.List;
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.locks.ReentrantLock;
      
      /**
       * LockableTask is a nice demonstration
       * @author Michael
       * @link https://stackoverflow.com/questions/32680954/reetrant-locks-in-java
       * @since 9/20/2015 11:25 AM
       */
      public class LockableTask implements Runnable {
      
          private static final int DEFAULT_WAIT = 100;
          private static final int DEFAULT_TIMEOUT = 1000;
          private static final int DEFAULT_THREADS = 5;
      
          private ReentrantLock lock;
          private int waitPeriod;
          private int timeoutPeriod;
          private boolean halfHeartedLockRequest;
      
          public static void main(String[] args) {
              long begTime = System.currentTimeMillis();
              System.out.println("Start reentrant lock test");
              try {
                  LockableTask lockableTask = new LockableTask(true, false);
                  int numThreads = (args.length > 0) ? Integer.parseInt(args[0]) : DEFAULT_THREADS;
                  List<Thread> threads = new ArrayList<>(numThreads);
                  for (int i = 0; i < numThreads; ++i) {
                      threads.add(new Thread(lockableTask));
                  }
                  for (Thread thread : threads) {
                      thread.start();
                  }
              } finally {
                  long endTime = System.currentTimeMillis();
                  System.out.println(String.format("Complete reentrant lock test in %d milliseconds", (endTime-begTime)));
              }
          }
      
          public LockableTask() {
              this(false, false, DEFAULT_WAIT, DEFAULT_TIMEOUT);
          }
      
          public LockableTask(boolean halfHeartedLockRequest, boolean fair) {
              this(halfHeartedLockRequest, fair, DEFAULT_WAIT, DEFAULT_TIMEOUT);
          }
      
          public LockableTask(boolean halfHeartedLockRequest, boolean fair, int waitPeriod, int timeoutPeriod) {
              this.halfHeartedLockRequest = halfHeartedLockRequest;
              this.lock = new ReentrantLock(fair);
              this.waitPeriod = (waitPeriod > 0) ? waitPeriod : DEFAULT_WAIT;
              this.timeoutPeriod = (timeoutPeriod > 0) ? timeoutPeriod : DEFAULT_TIMEOUT;
          }
      
          @Override
          public void run() {
              System.out.println(String.format("Thread '%s' requests lock ", Thread.currentThread().getName()));
              if (this.halfHeartedLockRequest) {
                  this.lock.tryLock();
              } else {
                  this.lock.lock();
              }
              try {
                  System.out.println(String.format("Thread '%s' acquires lock for %d ", Thread.currentThread().getName(), this.waitPeriod));
                  TimeUnit.MILLISECONDS.sleep(this.waitPeriod);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              } finally {
                  this.lock.unlock();
                  System.out.println(String.format("Thread '%s' releases lock ", Thread.currentThread().getName()));
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        ReentrantLock#tryLock() - 只有在调用时没有被另一个线程持有时才获取锁,如果成功则返回 true,否则返回 false。

        如果我们看到堆栈跟踪 -

        ...
        Now releasing lock Thread-2
        Exception in thread "Thread-2" java.lang.IllegalMonitorStateException
        ...
        Now releasing lock Thread-4
        Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
        ...
        

        更清楚的是,无法获得锁的线程在尝试释放它时引发异常。

        【讨论】:

        • 同意,我只是想知道如果tryLock() 无法获取锁,那么它会继续下一行吗?
        【解决方案4】:

        尝试这样的方式使其更清晰。

            System.out.println("Thread "+ Thread.currentThread().getName() + " trying to Acquire lock");
            if (lock.tryLock()) {
                try {
                    System.out.println("Lock acquired .. ");
                    int time=new Random().nextInt(10)+3;
                    System.out.println("Thread " + Thread.currentThread().getName() + " Acquired lock for " + time + " seconds.");
                    TimeUnit.SECONDS.sleep(time);
               } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
              }
                finally {
                    System.out.println("Now releasing lock "+Thread.currentThread().getName());
                    lock.unlock();
                }
            } else {
                System.out.println("Thread "+ Thread.currentThread().getName()+ " Failed to acquire lock .. ");
            }
        

        【讨论】:

          【解决方案5】:

          tryLock() 的调用只返回一个布尔值,它具有 “无轴承” 说明 是否 下一行/语句(如果有)将执行或不是。 这就是为什么应该使用 tryLock() 作为检查来执行一些想要同步的活动/代码块的原因。希望能回答你的问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多