java锁
锁是一种线程同步机制,例如同步块。 锁是使用同步块在内部实现的。 因此,我们可以在Java中使用锁代替同步关键字。 锁比同步块更灵活,更复杂。
从Java 5版本开始,JDK提供了几种锁实现,例如ReentrantReadWriteLock,ReentrantLock和StampedLock等。
1.同步和锁之间的区别
1)我们可以设置一个超时来使用Lock.tryLock(long timeout,TimeUnit timeUnit)方法访问资源,而同步是不可能的。
2)同步块必须完全包含在一个进程中。 锁可以包含在两个单独的进程中:lock()和unlock()。
3)处于“等待”状态以获取对同步块的访问权限的线程不能被中断。 Lock API提供了方法lockInterruptible(),该方法可在线程等待Lock时中断线程。
2.实现简单锁:
使用synced关键字可以实现简单的增量功能。
public class Counter {
private int number = 0;
public void increment() {
synchronized(number) {
return ++number;
}
}
}
让我们使用Lock接口转换上述程序。
public class Counter {
private int number = 0;
private Lock lock = new Lock();
public void increment() {
lock.lock();
int newNumber = ++number;
lock.unlock();
return new number;
}
}
在这里,我们使用的是锁接口,而不是synced关键字。
在增加数字之前,我们必须先锁定,以便其他任何人都不能在该块中输入任何内容,直到锁定增加并释放为止。
3.改进代码的方法
假设有些线程正在读取数据,有些线程正在向某些资源写入日期。 对于读取线程,如果一个线程正在从资源中读取数据,如果另一个线程也在从资源中读取数据,则不会造成任何问题,但是如果一个线程正在向资源中写入数据,则另一个线程也正在向资源中写入数据,则会引起问题。
- 对于读取操作,可以允许多个线程从资源读取数据,但不允许写入线程。
- 如果请求一个线程进行读取访问,而请求一个线程进行写入访问,那么优先级是什么,哪个线程可以访问资源?
- 如果请求一个线程进行读取访问,则请求第二个线程进行写入访问,如果请求读取访问的线程更多,则如果我们仅允许读取请求的线程,则写入线程将需要等待不确定的时间,这导致饥饿。
- 为了避免出现这种情况,Java在读取访问和写入访问时设置了一些规则。
3.1 ReadWriteLock
ReadWriteLock是Java 5版本中Java提供的实现; 它有两种方法,分别是Lock()和write-lock()。
Read Lock方法用于读取操作,而write-lock()方法用于写入操作。
3.2锁入
Java中的同步块是可重入的。 如果Java线程输入了同步的代码块,请在同步块的监视器对象上使用Lock。 然后,线程可以输入在同一监视对象上同步的其他Java代码块。
4.让我们考虑以下情形:
- Thread1正在获得读取访问权限。
- Thread2正在请求写访问权限; 由于只有一个阅读器,因此它将被阻止。
- 线程1再次请求读取访问权限,因为有一个写入请求,它将被阻止。
线程1和线程2都将被阻塞,从而导致死锁情况。
为了使锁重入,Java提供了另一种锁实现。
在使用重入锁定时,我们需要了解一些用例,例如,
在同一对象上,在某些方法中,线程可能会请求读取访问权限;在同一对象上,可能是线程在不同方法中,可能会请求写访问权限,反之亦然。
4.1阅读重入:
如果线程可以获取读访问权限(无写访问权限和写请求),或者该线程已经具有读访问权限,则该线程将被授予重入权限。 (无论是否有书面要求)。
4.2写重入:
仅当他们必须具有写访问权限时,才授予写重入权限。
4.3读写入口:
有时,线程必须同时读取和写入访问权限。 为此,线程必须是唯一的读取器。
4.4写读入口:
有时,必须具有写访问权的线程也需要读访问权。 如果有要求,应始终授予写者读访问权限。 如果该线程具有读取访问权限,则其他任何线程都不能具有读取或写入访问权限,因此这并不危险。
基于以上概念,让我们实现ArrayList的同步版本。
ThreadSaftyArrayList.java
public class ThreadSaftyArrayList {
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock writeLock = readWriteLock.writeLock();
private final Lock readLock = readWriteLock.readLock();
private List list = new ArrayList();
public E get(int index)
{
readLock.lock();
try {
return list.get(index);
} finally {
readLock.unlock();
}
}
public void set(E e)
{
writeLock.lock();
try {
list.add(e);
} finally {
writeLock.unlock();
}
}
public static void main(String[] args)
{
ThreadSaftyArrayList threadSafeArrayList = new ThreadSaftyArrayList();
threadSafeArrayList.set("1");
threadSafeArrayList.set("2");
threadSafeArrayList.set("3");
System.out.println("Printing the First Element : "+threadSafeArrayList.get(1));
}
}
注意:从最后一个子句中调用解锁():
当使用ReadWriteLock保护关键部分并且关键部分可能会引发异常时,从finally子句内部调用readUnlock()和writeUnlock()方法很重要。 这样做可以确保ReadWriteLock已解锁,以便其他线程可以锁定它。
伪代码:
lock.lockWrite();
try{
//do critical section code, which may throw an exception
} finally {
lock.unlockWrite();
}
5.结论
在当前的博客中,我们了解了锁,如何在Java中实现锁,如何替换了同步块。 我们还了解了不同类型的锁,例如读取锁和写入锁。 我们了解了Starvation(何时发生)以及Reentrance锁,它的用途以及Java中提供的实现类。 最后,我们使用ReentrantReadWriteLock类编写了示例ThreadSafty Arraylist类。 希望以上教程对Java开发人员和社区有所帮助。
这是Java中的锁的示例。