一个类中的Java锁对象
我正在尝试学习线程,关于下面的示例一个类中的Java锁对象,java,multithreading,methods,synchronization,locking,Java,Multithreading,Methods,Synchronization,Locking,我正在尝试学习线程,关于下面的示例 public class LockExample { private Lock lockone = new ReentrantLock(); public void method1() { lockone.lock(); try { // do something } finally { lockone.unlock(); }
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();
}
}
}
method1
和method2
,那么线程A
和B
不能同时访问method1
或method2
。但是如果我们使用不同的锁来锁定method1
和method2
,那么线程A
可以访问method1
,同时线程B
可以访问method2
如果对这两个方法使用相同的锁,那么如果线程1正在执行方法1(即,在获取锁之后),那么没有其他线程可以像方法2那样执行方法1 如果对2个方法使用2个不同的锁,那么如果线程1和线程2分别通过获取lock-1和lock-2上的锁来执行方法1和方法2,那么其他线程可以在线程1释放锁时执行方法1,在线程2释放锁时执行方法2 这是否意味着如果我们使用相同的锁锁定method1和method2,那么线程A和B不能同时访问method1或method2。但是如果我们使用不同的锁lockone和locktwo锁定method1和method2,那么threadA可以访问method1,而thread BCA可以访问method2 是,如果method1和method2使用相同的锁,那么线程A和B不能同时访问method1或method2。但是如果方法使用不同的锁,那么线程A和线程B将不能访问相同的方法,但是访问不同的方法将起作用。也就是说,线程A和线程B不能访问相同的方法1或相同的方法2。但是当线程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();
}
}
为什么我们不能,或者我们可以通过在method1中获取锁,然后在method2之后释放锁,将method1和method2变成一个关键部分
因为这是个糟糕的设计
我工作的办公室有数十万行Java代码需要维护。其中一些已经存在了十年之久,开发人员一直在来来往往地使用这些代码。遵守严格的风格规则和严格的惯例是我们如何保持一切安全、理智和基本上没有bug的重要组成部分
如果我们使用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()的情况下调用methodOne()。它保证没有这样的函数可以在不首先调用methodOne()的情况下调用methodTwo()。它更容易理解,也更容易维护。您发布的第二段代码没有意义。例如,如果一个线程调用
method2
而不持有锁(即以前没有调用method1
),则会抛出IllegalMonitorStateException
。@CristianGreco-我想OP是在试图解释他的逻辑,而不是实际代码:)也许这对您来说是显而易见的,但以防万一不是这样:LockExample类的每个实例都有自己单独的ReentrantLock对象。因此,如果有两个LockExample对象a和b,则一个线程可能位于a.method1()中,而另一个线程位于b.method2()中。再一次,如果这是显而易见的,我很抱歉,但这似乎是一个很多初学者不容易理解的想法。谢谢!但是为什么我们不能,或者我们可以通过在method1中获取锁,然后在method2之后释放锁,将method1和method2变成一个关键部分呢?对不起,我看不出这有什么问题…@stillAFanOfTheSimpsons-不,我们不能这么做。同步建立了“先发生后发生”关系。实际上,当JVM将执行指令时,它可以在同步块内或同步块(或方法)外重新排序指令,但不能在同步块之间重新排序,以提高性能。在这种情况下,任何线程都可以只调用method2,而不调用method1。在第二种情况下,任何线程都必须首先调用method1,然后调用method2。否则将抛出非法监视异常。
private methodOne(...) { ... }
private methodTow(...) { ... }
public callMethodOneAndMethodTwo(...) {
lockOneTwo.lock();
try {
methodOne(...);
methodTwo(...);
} finally {
lockOneTwo.unlock();
}