Java并发Lock接口

简介:Lock 接口实现类提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition (Condition实现类ConditonObject来实现线程的通知/与唤醒机制,关于Condition后期会进行介绍)对象。
锁是用于控制多线程访问共享资源的工具。通常,锁提供对共享资源的独占访问:一次只有一个线程可以获取锁,对共享资源的所有访问都需要首先获取锁。但是,一些锁可以允许同时访问共享资源,例如ReadWriteLock
虽然使用关键字synchronized修饰的方法或代码块,会使得在监视器模式(ObjectMonitor)下编程变得非常容易(通过synchronized块或者方法所提供的隐式获取释放锁的便捷性)。虽然这种方式简化了锁的管理,但是某些情况下,还是建议采用Lock接口(及其相关子类)提供的显示的锁的获取和释放。例如,针对一个场景,手把手进行锁获取和释放,先获得锁A,然后再获取锁B,当锁B获得后,释放锁A同时获取锁C,当锁C获得后,再释放B同时获取锁D,以此类推。这种场景下,synchronized关键字就不那么容易实现了,而Lock接口的实现类允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁。
方法

方法描述
void lock()获得锁
void lockInterruptibly()获取锁定,除非当前线程中断
Condition newCondition()返回绑定到此Lock实例的新Condition实例
boolean tryLock()只有在调用时才可以获得锁
boolean tryLock(long time, TimeUnit unit)如果在给定的等待时间内自由,并且当前线程未被中断,则获取该锁。
void unlock()释放锁

Lock相关实现类关系图
在这里插入图片描述
ReentrantLock类作为Lock接口的一个实现。 ReentrantLock类允许线程锁定方法,即使它已经具有其他方法锁。 这里我们使用ReentrantLock类下的lock()获取锁和unlock()来释放锁进行一个案例展示
代码

public class PrintDemoLock {
	private final Lock queueLock = new ReentrantLock();
	
	public void print(){
		queueLock.lock();
		try {
			Long duration = (long)(Math.random()*10000);
			System.out.println(Thread.currentThread().getName()
					+ " Time Taken" + (duration/1000)+ " seconds.");
			Thread.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
			System.out.printf("%s printed the document successfully.\n", Thread.currentThread().getName());
			queueLock.unlock();
		}
	}
}
class ThreadDemo extends Thread{
	PrintDemoLock printDemoLock;
	ThreadDemo(String name,PrintDemoLock printDemoLock){
		super(name);
		this.printDemoLock = printDemoLock;
	}
	public void run(){
		System.out.printf("%s starts printing a document\n",Thread.currentThread().getName());
		printDemoLock.print();
	}
}
public class TestLock {
	public static void main(String[] args) {
		PrintDemoLock printDemoLock = new PrintDemoLock();
		
		ThreadDemo t1 =new ThreadDemo("Thread - 1", printDemoLock);
		ThreadDemo t2 =new ThreadDemo("Thread - 2", printDemoLock);
		ThreadDemo t3 =new ThreadDemo("Thread - 3", printDemoLock);
		ThreadDemo t4 =new ThreadDemo("Thread - 4", printDemoLock);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
	}
}

结果:
在这里插入图片描述
总结:
1、Lock接口(及其实现类)相比synchronized有如下优点:

  • 锁的释放与获取不在是隐式的,允许锁在不同的作用范围内获取和释放`,并允许以任何顺序获取和释放多个锁。
  • 能被中断的获取锁,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常会被抛出,同时锁也会被释放。
  • 超时获取锁:在指定的截止时间之前获取锁,如果截止时间到了任然无法获取到锁,则返回。
    在使用Lock的时候注意,一定要确保必要时释放锁。

2、当锁定和解锁发生在不同的范围时,一定要注意确保在持有锁时执行的所有代码都受到try-finally或try-catch的保护,以确保在必要时释放锁。不要将获取锁的过程写在try块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放(因为一旦发生异常,就会走finally语句,如果这个异常(可能是用户自定义异常,用户可以自己处理)需要线程1来处理,但是接着执行了lock.unlock()语句导致了锁的释放。那么其他线程就可以操作共享资源。有可能破坏程序的执行结果)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的Lock接口提供了比synchronized关键字更加灵活和强大的线程同步机制。下面是Lock接口的常用方法: 1. lock():获取锁,如果锁已经被其他线程占用,则当前线程会被阻塞。 2. tryLock():尝试获取锁,如果锁没有被其他线程占用,则获取锁成功并返回true;否则返回false,不会阻塞当前线程。 3. tryLock(long time, TimeUnit unit):在指定时间内尝试获取锁,如果在指定时间内获取到锁,则返回true;否则返回false,不会阻塞当前线程。 4. unlock():释放锁,如果当前线程持有锁,则释放锁;否则会抛出IllegalMonitorStateException异常。 Lock接口的实现类包括ReentrantLock、ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock等。其中ReentrantLock是最基本的实现类,而ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock是对读写锁的支持。 使用Lock接口进行线程同步时,需要注意以下几点: 1. 获取锁后必须在finally块中释放锁,否则可能导致死锁。 2. 不要使用Lock接口代替synchronized关键字进行简单的线程同步,因为Lock接口需要手动释放锁,容易出错。 3. 在使用ReentrantLock实现线程同步时,如果获取锁的线程出现异常而没有释放锁,则可能会导致其他线程一直阻塞,从而产生死锁。为了避免这种情况,建议使用try-finally块释放锁,或者使用Lock接口提供的unlock()方法释放锁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值