2014-12-04 76 views
1

这是Oracle关于Lock对象的java教程中的例子。有人请确认我是否正确解释了代码。为什么线程在释放锁之后仍然运行?

我会考虑只有第一个线程,因为其他工作方式相同。

首先,它获取alphonse的锁,并访问方法impendingBow。该方法现在尝试将两个实例的两个锁分配给该线程。如果线程无法获取这两个锁,它将释放它获取的锁。这是当我抚摸。如果线程释放锁,另一个可以访问这两个实例,并且第一个线程在释放锁之后应该停止执行。但事实并非如此。它仍然返回布尔值并继续在bow方法中运行else语句。为什么可能发生?我认为就像同步代码一样,线程应该停止执行,直到它再次获得锁定。

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

public class Safelock { 
    static class Friend { 
     private final String name; 
     private final Lock lock = new ReentrantLock(); 

     public Friend(String name) { 
      this.name = name; 
     } 

     public String getName() { 
      return this.name; 
     } 

     public boolean impendingBow(Friend bower) { 
      Boolean myLock = false; 
      Boolean yourLock = false; 
      try { 
       myLock = lock.tryLock(); 
       yourLock = bower.lock.tryLock(); 
      } finally { 
       if (! (myLock && yourLock)) { 
        if (myLock) { 
         lock.unlock(); 
        } 
        if (yourLock) { 
         bower.lock.unlock(); 
        } 
       } 
      } 
      return myLock && yourLock; 
     } 

     public void bow(Friend bower) { 
      if (impendingBow(bower)) { 
       try { 
        System.out.format("%s: %s has" 
         + " bowed to me!%n", 
         this.name, bower.getName()); 
        bower.bowBack(this); 
       } finally { 
        lock.unlock(); 
        bower.lock.unlock(); 
       } 
      } else { 
       System.out.format("%s: %s started" 
        + " to bow to me, but saw that" 
        + " I was already bowing to" 
        + " him.%n", 
        this.name, bower.getName()); 
      } 
     } 

     public void bowBack(Friend bower) { 
      System.out.format("%s: %s has" + 
       " bowed back to me!%n", 
       this.name, bower.getName()); 
     } 
    } 

    static class BowLoop implements Runnable { 
     private Friend bower; 
     private Friend bowee; 

     public BowLoop(Friend bower, Friend bowee) { 
      this.bower = bower; 
      this.bowee = bowee; 
     } 

     public void run() { 
      Random random = new Random(); 
      for (int n = 0; n <5;n++) { 
       try { 
        Thread.sleep(Math.round(Math.random()*1000)); 
       } catch (InterruptedException e) {} 
       bowee.bow(bower); 
      } 
     } 
    } 


    public static void main(String[] args) { 
     final Friend alphonse = 
      new Friend("Alphonse"); 
     final Friend gaston = 
      new Friend("Gaston"); 
     new Thread(new BowLoop(alphonse, gaston)).start(); 
     new Thread(new BowLoop(gaston, alphonse)).start(); 
    } 
} 
+0

是什么让你认为一个线程应该停止执行_after_释放一个'Lock'对象? – 2014-12-04 16:37:15

+0

我将锁对象与同步代码关联起来,它只允许线程在获取锁的情况下运行代码,并且如果线程没有锁,则会阻塞线程。 – PMH 2014-12-04 16:41:30

+0

它如何获得一个锁,如果它没有运行? – 2014-12-04 16:42:46

回答

0

同步块Lock.lock(),Lock.tryLock()Lock.tryLock(timeout)之间存在细微的差异。

tryLock方法只有在调用时它不被另一个线程占用的情况下才会获取该锁。所以当它无法获得锁时,它立即返回。

lock方法将等待锁定可用,同时等待该线程处于休眠状态。代码与synchronized语句同步也是如此。线程将等待,直到它可以获得该代码块的锁定。

tryLock(timeout)方法将一直等到锁定可用或直到超时过期。

或多或少的Javadoc描述:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html

对于您预期改变tryLock语句lock,你会发现,该线程将进入一个僵局,最终的行为。

+0

你说过:tryLock方法只有在调用时没有被另一个线程占用的情况下才会获取该锁。所以当它无法获得锁时,它立即返回。你什么意思是“立即返回”?这意味着该方法继续运行其余的代码? – PMH 2014-12-04 16:53:02

+0

是的。那是对的。该方法立即返回,即它不停止等待锁定变为可用。你可以通过查看存储在'myLock'和'yourLock'中的返回值来告诉它获得了锁。 在这个例子中,你可以用'tryLock(timeout)'替换'tryLock'。然后在语句中添加一些代码来衡量所花费的时间。你会发现'tryLock'永远不会花费很长时间,'tryLock(timeout)'通常会和超时时间一样长。 – mpkorstanje 2014-12-04 18:57:08

0

线程在释放锁时不会停止执行。它停止执行,如果:

  1. 它试图锁定一个锁,但不能(也不会在这里,因为你使用的tryLock停止执行)
  2. 它明确地得到控制,例如通过睡眠(睡眠())
  3. 调度程序决定它现在更喜欢另一个线程。对于所有的意图和目的,你应该认为这是一个随机事件。

这些事情都没有发生在这里,所以一个线程继续执行直到3)发生。

相关问题