相信大家对Java ReentrantLock使用都有了一定的了解,ReentrantLock 类实现了 Lock 接口,并在访问共享资源时为方法提供同步。操作共享资源的代码被锁定和解锁方法的调用包围。这为当前工作线程提供了一个锁定,并阻止了所有其他试图锁定共享资源的线程。
顾名思义,ReentrantLock 允许线程多次进入资源锁。当线程第一次进入锁时,保持计数设置为 1。在解锁之前,线程可以重新进入锁,每次保持计数加一。对于每个解锁请求,保持计数减 1,当保持计数为 0 时,资源被解锁。
可重入锁还提供了一个公平参数,通过该参数,锁将遵循锁请求的顺序,即在线程解锁资源后,锁将转到等待时间最长的线程。这种公平模式是通过将 true 传递给锁的构造函数来设置的。
这些锁的使用方式如下:
public void some_method()
{
reentrantlock.lock();
try
{
//Do some work
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
reentrantlock.unlock();
}
}
总是在 finally 块中调用解锁语句,以确保即使在方法体(try 块)中抛出异常也能释放锁。
lock():调用 lock() 方法将保持计数加 1,如果共享资源最初是空闲的,则将锁分配给线程。
unlock():调用unlock()方法将持有计数减1。当这个计数达到零时,资源被释放。
tryLock():如果资源未被任何其他线程持有,则调用 tryLock() 返回 true,并且持有计数加 1。如果资源不是空闲的,则该方法返回 false,线程不会被阻塞,而是退出。
tryLock(long timeout, TimeUnit unit):根据方法,线程在退出前等待方法参数定义的一定时间段来获取资源的锁。
lockInterruptibly():如果资源空闲,则此方法获取锁,同时允许线程在获取资源时被其他线程中断。意思是如果当前线程正在等待锁,但是其他线程请求锁,那么当前线程将被中断并立即返回而不获取锁。
getHoldCount():此方法返回资源上持有的锁的数量。
isHeldByCurrentThread():如果当前线程持有资源的锁,则此方法返回 true。
在下面的教程中,我们将看一个可重入锁的基本示例。
应遵循的步骤
1.创建ReentrantLock的对象
2.创建一个worker(Runnable Object)来执行并将锁传递给对象
3.使用lock()方法获取共享资源的锁
4.工作完成后调用unlock()方法释放锁
下面是问题陈述的实现:
// Java code to illustrate Reentrant Locks
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
class worker implements Runnable
{
String name;
ReentrantLock re;
public worker(ReentrantLock rl, String n)
{
re = rl;
name = n;
}
public void run()
{
boolean done = false;
while (!done)
{
//Getting Outer Lock
boolean ans = re.tryLock();
// Returns True if lock is free
if(ans)
{
try
{
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("task name - "+ name
+ " outer lock acquired at "
+ ft.format(d)
+ " Doing outer work");
Thread.sleep(1500);
// Getting Inner Lock
re.lock();
try
{
d = new Date();
ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("task name - "+ name
+ " inner lock acquired at "
+ ft.format(d)
+ " Doing inner work");
System.out.println("Lock Hold Count - "+ re.getHoldCount());
Thread.sleep(1500);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
finally
{
//Inner lock release
System.out.println("task name - " + name +
" releasing inner lock");
re.unlock();
}
System.out.println("Lock Hold Count - " + re.getHoldCount());
System.out.println("task name - " + name + " work done");
done = true;
}
catch(InterruptedException e)
{
e.printStackTrace();
}
finally
{
//Outer lock release
System.out.println("task name - " + name +
" releasing outer lock");
re.unlock();
System.out.println("Lock Hold Count - " +
re.getHoldCount());
}
}
else
{
System.out.println("task name - " + name +
" waiting for lock");
try
{
Thread.sleep(1000);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
public class test
{
static final int MAX_T = 2;
public static void main(String[] args)
{
ReentrantLock rel = new ReentrantLock();
ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
Runnable w1 = new worker(rel, "Job1");
Runnable w2 = new worker(rel, "Job2");
Runnable w3 = new worker(rel, "Job3");
Runnable w4 = new worker(rel, "Job4");
pool.execute(w1);
pool.execute(w2);
pool.execute(w3);
pool.execute(w4);
pool.shutdown();
}
}
样品执行
输出:
任务名称 - Job2 等待锁定
任务名称 - Job1 外部锁在 09:49:42 获取
任务名称 - Job2 等待锁定
任务名称 - Job1 内部锁在 09:49:44 获得
锁定保持计数 - 2
任务名称 - Job2 等待锁定
任务名称 - Job2 等待锁定
任务名称 - Job1 释放内部锁
锁定保持计数 - 1
任务名称 - Job1 完成的工作
任务名称 - Job1 释放外部锁
锁定保持计数 - 0
任务名称 - Job3 外部锁在 09:49:45 获取
任务名称 - Job2 等待锁定
任务名称 - 在 09:49:47 获得的 Job3 内部锁正在做内部工作
锁定保持计数 - 2
任务名称 - Job2 等待锁定
任务名称 - Job2 等待锁定
任务名称 - Job3 释放内部锁
锁定保持计数 - 1
任务名称 - Job3 完成的工作
任务名称 - Job3 释放外锁
锁定保持计数 - 0
任务名称 - Job4 外部锁在 09:49:48 获取
任务名称 - Job2 等待锁定
任务名称 - Job4 内部锁在 09:49:50 获取
锁定保持计数 - 2
任务名称 - Job2 等待锁定
任务名称 - Job2 等待锁定
任务名称 - Job4 释放内部锁
锁定保持计数 - 1
任务名称 - Job4 完成的工作
任务名称 - Job4 释放外锁
锁定保持计数 - 0
任务名称 - Job2 外部锁在 09:49:52 获取
任务名称 - Job2 内部锁在 09:49:53 获取
锁定保持计数 - 2
任务名称 - Job2 释放内部锁
锁定保持计数 - 1
任务名称 - Job2 完成的工作
任务名称 - Job2 释放外部锁
锁定保持计数 - 0
1.人们可能会忘记在 finally 块中调用 unlock() 方法,从而导致程序出现错误。确保在线程退出之前释放锁。
2.用于构造锁对象的公平参数会降低程序的吞吐量。
ReentrantLock 是同步的更好替代品,它提供了许多 synchronized 没有提供的功能。然而,这些明显好处的存在并不足以成为总是喜欢 ReentrantLock 进行同步的充分理由。相反,根据您是否需要 ReentrantLock 提供的灵活性来做出决定。
以上就是关于“告诉你什么是Java中的可重入锁”的介绍,大家如果对此比较感兴趣,想了解更多相关知识,可以关注一下极悦的Java多线程编程,里面有更丰富的知识等着大家去学习,相信对大家一定会有所帮助的哦。
你适合学Java吗?4大专业测评方法
代码逻辑 吸收能力 技术学习能力 综合素质
先测评确定适合在学习