告诉你什么是Java中的可重入锁 - 极悦
首页 课程 师资 教程 报名

告诉你什么是Java中的可重入锁

  • 2022-09-08 11:12:49
  • 1366次 极悦

什么是可重入锁?

相信大家对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 块)中抛出异常也能释放锁。

ReentrantLock() 方法

lock():调用 lock() 方法将保持计数加 1,如果共享资源最初是空闲的,则将锁分配给线程。

unlock():调用unlock()方法将持有计数减1。当这个计数达到零时,资源被释放。

tryLock():如果资源未被任何其他线程持有,则调用 tryLock() 返回 true,并且持有计数加 1。如果资源不是空闲的,则该方法返回 false,线程不会被阻塞,而是退出。

tryLock(long timeout, TimeUnit unit):根据方法,线程在退出前等待方法参数定义的一定时间段来获取资源的锁。

lockInterruptibly():如果资源空闲,则此方法获取锁,同时允许线程在获取资源时被其他线程中断。意思是如果当前线程正在等待锁,但是其他线程请求锁,那么当前线程将被中断并立即返回而不获取锁。

getHoldCount():此方法返回资源上持有的锁的数量。

isHeldByCurrentThread():如果当前线程持有资源的锁,则此方法返回 true。

ReentrantLock() 示例

在下面的教程中,我们将看一个可重入锁的基本示例。

应遵循的步骤

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大专业测评方法

代码逻辑 吸收能力 技术学习能力 综合素质

先测评确定适合在学习

在线申请免费测试名额
价值1998元实验班免费学
姓名
手机
提交