Java锁示例– ReentrantLock

时间:2020-02-23 14:36:41  来源:igfitidea点击:

欢迎使用Java Lock示例教程。
通常在使用多线程环境时,为了确保线程安全,我们使用了同步。

Java锁

在大多数情况下,synchronized关键字是解决之道,但它有一些缺点,导致将Lock API包含在Java Concurrency包中。
Java 1.5 Concurrency API带有带有Lock接口的java.util.concurrent.locks包和一些实现类,用于改进对象锁定机制。

Java Lock API中的一些重要接口和类是:

  • 锁定:这是锁定API的基本接口。
    它提供了synced关键字的所有功能,并提供了其他方式来创建不同的锁定条件,从而为线程等待锁定提供了超时。
    一些重要的方法是:lock()获取锁,unlock()释放锁,tryLock()等待锁一定时间,newCondition()创建Condition等。

  • 条件:条件对象类似于对象等待通知模型,具有附加功能以创建不同的等待集。
    Condition对象始终由Lock对象创建。
    一些重要的方法是类似于wait()和signal()的await(),类似于notify()和notifyAll()方法的signalAll()。

  • ReadWriteLock:它包含一对关联的锁,一个用于只读操作,另一个用于写入。
    只要没有写程序线程,读锁就可以同时由多个读程序线程持有。
    写锁是排他的。

  • ReentrantLock:这是Lock接口使用最广泛的实现类。
    此类以与synced关键字相似的方式实现Lock接口。
    除了Lock接口的实现外,ReentrantLock还包含一些实用程序方法来获取持有锁的线程,等待获取锁的线程等。
    同步块本质上是可重入的,即,如果线程在监视对象上具有锁,并且另一个同步块需要将锁锁定在同一监视器对象上,则线程可以输入该代码块。
    我认为这就是类名称为ReentrantLock的原因。
    让我们通过一个简单的示例来了解此功能。

如果线程输入foo(),则它具有Test对象的锁定,因此,当它尝试执行bar()方法时,由于该线程已经持有Test对象的锁定,因此允许该线程执行bar()方法。
已同步(此)。

Java锁示例– Java中的ReentrantLock

现在,让我们看一个简单的示例,我们将用Java Lock API替换synced关键字。

假设我们有一个Resource类,其中包含一些我们希望它是线程安全的操作以及一些不需要线程安全的方法。

public class Test{

public synchronized foo(){
  //do something
  bar();
}

public synchronized bar(){
  //do some more
}
}

现在假设我们有一个Runnable类,我们将其中使用Resource方法。

package com.theitroad.threads.lock;

public class Resource {

	public void doSomething(){
		//do some operation, DB read, write etc
	}
	
	public void doLogging(){
		//logging, no need for thread safety
	}
}

请注意,我正在使用同步块来获取对Resource对象的锁定。
我们可以在类中创建一个虚拟对象,并将其用于锁定目的。

现在,让我们看看如何使用Java Lock API并在不使用synced关键字的情况下重写上述程序。
我们将在Java中使用ReentrantLock。

package com.theitroad.threads.lock;

public class SynchronizedLockExample implements Runnable{

	private Resource resource;
	
	public SynchronizedLockExample(Resource r){
		this.resource = r;
	}
	
	@Override
	public void run() {
		synchronized (resource) {
			resource.doSomething();
		}
		resource.doLogging();
	}
}

如您所见,我正在使用tryLock()方法来确保我的线程仅等待一定的时间,并且如果它没有获得对象的锁,那么它只是在记录并退出。
要注意的另一个重要点是,即使doSomething()方法调用引发任何异常,也要使用try-finally块来确保释放锁定。

Java Lock与同步

基于以上详细信息和程序,我们可以轻松得出Java Lock和同步之间的以下差异。

  • Java Lock API提供了更多的可见性和锁定选项,与同步不同,在同步中线程可能最终无限期地等待锁,我们可以使用tryLock()来确保线程仅在特定时间等待。

  • 同步代码更简洁,易于维护,而使用Lock时,即使在lock()和unlock()方法调用之间引发了某些异常,我们也不得不尝试最终锁定以确保释放Lock。

  • 同步块或者方法只能覆盖一个方法,而我们可以使用Lock API在一个方法中获取锁,然后在另一方法中释放锁。

  • synced关键字不提供公平性,但是我们可以在创建ReentrantLock对象时将公平性设置为true,以便等待时间最长的线程首先获得该锁。

  • 我们可以为Lock创建不同的条件,并且不同的线程可以为不同的条件使用await()。