2014-10-18 53 views
1

我努力学习线程,以及关于下面的例子的Java锁对象

public class LockExample { 
    private Lock lockone = new ReentrantLock(); 

    public void method1() { 
     lockone.lock(); 
     try { 
      // do something 
     } finally { 
      lockone.unlock(); 
     } 
    } 

    public void method2() { 
     lockone.lock(); 
     try { 
      // do something 
     } finally { 
      lockone.unlock(); 
     } 
    } 
} 
  1. 意思是,如果我们使用相同的锁锁method1method2,说线程AB不能同时访问method1method2。但如果我们锁定method1method2使用不同的锁lockonelocktwo,那么线程A可以访问method1,同时线程B可以访问method2

  2. 为什么我们不分别锁定每个方法而不是将它们放在一个锁中?

public class LockExample { 
    private Lock lockone = new ReentrantLock(); 

     public void method1() { 
      lockone.lock(); 
      try { 
       // do something 
      } // wrap the two methods in one lock? 
     } 

     public void method2() { 

      try { 
       // do something 
      } finally { 
       lockone.unlock(); 
      } 
     } 
    } 
} 
+0

您发布的代码的第二部分没有意义。例如,如果一个线程在不保持锁的情况下调用'method2'(即没有先前调用'method1'),则抛出'IllegalMonitorStateException'。 – 2014-10-18 10:26:19

+0

@CristianGreco - 我认为OP正试图解释他的逻辑,而不是实际的代码:) – TheLostMind 2014-10-18 10:27:18

+0

也许这对你来说很明显,但以防万一它不是:你的LockExample类的每个实例都有自己单独的ReentrantLock对象。所以如果你有两个LockExample对象,a和b,它可以让一个线程在a.method1()中,而另一个线程在b.method2()中。再说一次,很抱歉,如果这是显而易见的,但这似乎是一个很多初学者不容易掌握的想法。 – 2014-10-18 15:25:34

回答

1

是否意味着如果我们使用相同的锁来锁定方法1和方法2,则说线程A和B不能同时访问方法1或方法2。但是如果我们使用不同的锁lockone和locktwo锁定method1和method2,那么threadA可以访问method1,同时线程Bcan访问method2?

是的,如果method1和method2使用相同的锁,那么线程A和B不能同时访问方法1或方法2。但是如果方法使用不同的锁,那么线程A和B将无法访问相同的方法,但访问不同的方法将工作。也就是说,线程A和B不能访问相同的method1或相同的method2。但是当线程A访问方法1时,线程B可以访问方法2。

为什么我们不分别锁定每个方法而不是将它们放在一个锁中?

如果你想让任何线程阻塞方法2访问,直到第一个线程还没有完成对方法1和方法2的访问/执行,那么给定的代码示例是正确的。

例子:

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


public class Main implements Runnable { 
    private Lock lock = new ReentrantLock(); 

    public void method1() throws InterruptedException { 
     System.out.println(Thread.currentThread().getName() + " entered method1."); 

     Thread.sleep(1000); 

     lock.lock(); 

     System.out.println(Thread.currentThread().getName() + " ackquired lock."); 

     Thread.sleep(1000); 
    } 

    public void method2() { 
     System.out.println(Thread.currentThread().getName() + " entered method2."); 

     lock.unlock(); 

     System.out.println(Thread.currentThread().getName() + " released lock."); 
    } 

    public void run() { 
     try{ 
      method1(); 
     }catch(Exception e){ 
      e.printStackTrace(); 
     }finally{ 
      method2(); 
     } 
    } 

    public static void main(String [] args) { 
     Runnable runnable = new Main(); 

     new Thread(runnable, "ThreadA").start(); 
     new Thread(runnable, "ThreadB").start(); 
    } 
} 
+0

在这种情况下,任何线程只能调用method2,而不调用method1? – stillAFanOfTheSimpsons 2014-10-18 11:01:36

+1

没有。在第二种情况下,任何线程都必须先调用方法1然后调用方法2。否则IllegalMonitorException将被抛出。 – UDPLover 2014-10-18 11:04:31

0

如果使用相同的锁对于两种方法,这时如果线程1被执行方法-1(即,获得锁之后),则没有其他线程可以执行方法-1以及方法-2。

如果对2种方法使用2种不同的锁,那么如果线程1和线程2分别通过获取锁1和锁2来执行方法1和方法2,则其他线程可以执行如果线程1释放锁,则方法-1;如果线程2释放锁,则方法2。

+0

谢谢!但是,为什么我们不能,或者只能通过获取methos1中的锁并在method2之后释放锁,将方法1和方法2合并为一个关键部分?对不起,我看不到这有什么问题... – stillAFanOfTheSimpsons 2014-10-18 10:37:55

+0

@stillAFanOfTheSimpsons - 不,我们做不到。同步建立*发生之前*关系。在深层次,当JVM执行指令时,它可以在同步块内或同步块(或方法)外重新排序*指令,但不会在同步块之间进行重新排序以提高性能。 – TheLostMind 2014-10-18 10:41:20

0

我们为什么不,或者我们可以使方法1和2到一个临界区仅收购methos1锁,并释放之后方法2锁?

因为它是坏的设计。

我工作的办公室有数十万行Java代码要维护。其中一些已经存在了长达十年的时间,开发人员一直在来回运行代码。遵守严格的样式规则和严格的约定是我们如何保持一切安全和健全并且大多无缺陷的重要组成部分。

如果我们使用的ReentrantLock,我们使用总是使用它,你如何在你的第一个例子中使用它:

lockone.lock(); 
try { 
    // do something 
} finally { 
    lockone.unlock(); 
} 

如果有一个methodOne和methodTwo,而且也需要给他们打电话两个原子,那么我们把它写这样的:

private methodOne(...) { ... } 
private methodTow(...) { ... } 
public callMethodOneAndMethodTwo(...) { 
    lockOneTwo.lock(); 
    try { 
     methodOne(...); 
     methodTwo(...); 
    } finally { 
     lockOneTwo.unlock(); 
    } 

管理锁保证没有线程可以永远不能以解除锁定它做什么之后,它需要做的这样。它保证任何其他包中的其他函数都可以调用methodOne(),而不会随后调用methodTwo()。它保证没有这样的函数没有首先调用methodOne()就可以调用methodTwo()。它更容易理解,并且更容易维护。