Java多线程(3)——Lock

以下是摘自JAVA API文档关于lock和synchronized的一段话:

  • Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关Condition对象。
  • synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。
  • Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。

先看一下Lock接口的定义的方法:

  •  void lock()  获取锁。 
  • void lockInterruptibly()   如果当前线程未被中断,则获取锁
  • Condition newCondition()  返回绑定到此 Lock 实例的新 Condition 实例
  • boolean tryLock()   仅在调用时锁为空闲状态才获取该锁
  • boolean tryLock(long time, TimeUnit unit)   如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
  • void unlock()   释放锁

 注意:synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

Lock接口的实现类:ReentrantLock,   ReentrantReadWriteLock.ReadLock,   ReentrantReadWriteLock.WriteLock 。其中ReadLock和WriteLock是ReentrantReadWriteLock的内部类,ReentrantReadWriteLock是ReadWriteLock的实现类。关于ReadWriteLock后续再做详解。

ReentrantLock

一个可重入的互斥锁,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似轮询锁、定时锁等候和可中断锁等候的一些特性。构造方法:

  • ReentrantLock()  创建一个 ReentrantLock 的实例 
  • ReentrantLock(boolean fair)   创建一个具有给定公平策略的 ReentrantLock----公平锁

ReentrantLock特有的方法:

  • int getHoldCount()  查询当前线程保持此锁的次数。 
  • int getQueueLength()  返回正等待获取此锁的线程估计数
  • int getWaitQueueLength(Condition condition)  返回等待与此锁相关的给定条件的线程估计数
  • boolean hasQueuedThread(Thread thread)   查询给定线程是否正在等待获取此锁
  • boolean hasQueuedThreads()  查询是否有些线程正在等待获取此锁
  • boolean hasWaiters(Condition condition)  查询是否有些线程正在等待与此锁有关的给定条件
  • boolean isFair()  如果此锁的公平设置为 true,则返回 true
  • boolean isHeldByCurrentThread()  查询当前线程是否保持此锁
  • boolean isLocked()  查询此锁是否由任意线程保持

如下实现两个线程轮询处理公共数据:

public class ReenLockTest {
	
	public static void main(String[] args) {
		new Thread(new Runnable(){
			@Override
			public void run() {
				Thread1.m1();			
			}			
		},"thread1").start();
		new Thread(new Runnable(){
			@Override
			public void run() {
				Thread1.m2();			
			}			
		},"thread2").start();
		
	}
	static ReentrantLock lock = new ReentrantLock();
	static ReentrantLock lock2 = new ReentrantLock();
	static int i = 100;
	static class Thread1{
		public static void m1(){
			while(i>0){
				lock.lock();
				try {				
					System.out.println("method m1()" + i--);					
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if(!lock2.isLocked())
						lock2.unlock();
				}
			}
		}
		
		public static void m2(){
			while(i>0){
				lock2.lock();
				try {
					System.out.println("method m2()" + i--);					
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if(!lock.isLocked())
						lock.unlock();
				}
			}
		}		
	}
}

重入锁:

ReentrantLock锁,可以被单个线程多次获取:

ReentrantLock部分方法介绍:

1、tryLock():

  • boolean tryLock()   尝试获取锁,仅在调用时锁为空闲状态才获取该锁。
  • boolean tryLock(long timeout, TimeUnit unit)  如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。 

如果该锁没有被另一个线程保持,并且立即返回 true 值,则将锁的保持计数设置为 1。即使已将此锁设置为使用公平排序策略,但是调用 tryLock() 立即获取锁(如果有可用的),而不管其他线程当前是否正在等待该锁。

  • 如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true
  • 如果锁被另一个线程保持,则此方法将立即返回 false 值。

tryLock()的用法与lock()的使用方式基本相同,只是在释放锁时,tryLock()方式获取的锁需要先验证当前线程是否获取到锁isLocked()

static class Thread1{
		public static void m1(){
			while(i>0){
				boolean tryLock = lock.tryLock();
				if(tryLock){
					try {	
						Thread.sleep(500);
						System.out.println("method m1()" + i--);					
					} catch (Exception e) {
						e.printStackTrace();
					}finally{
						if(lock.isLocked())
							lock.unlock();
					}
				}
			}
		}

2、lockInterruptibly()  

  • void lockInterruptibly()  如果当前线程未被中断,则获取锁

  1. 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。 
  2. 如果当前线程已经保持此锁,则将保持计数加 1,并且该方法立即返回。
  3. 如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态: 
                                   锁由当前线程获得;
                                  其他某个线程中断当前线程。
  4. 如果当前线程获得该锁,则将锁保持计数设置为 1。 
  5. 如果当前线程在进入此方法时已经设置了该线程的中断状态;或者 在等待获取锁的同时被中断。 则抛出 InterruptedException,并且清除当前线程的已中断状态。 

lockInterruptibly() 与lock() 的区别:

  • lock 优先考虑获取锁,待获取锁成功后,才响应中断。使用lock()方法获取锁标记,如果需要阻塞等待锁标记,不能被打断
  • lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。使用lockInterribly()方法获取锁标记,如果需要阻塞等待锁标记,可以被打断。
public class TryLockTest {
	static Lock lock = new ReentrantLock();
	static Lock lock2 = new ReentrantLock();
	
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				m1();
			}			
		});
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				m2();
			}			
		});
		t1.start();
		t2.start();
		t1.interrupt();
		t2.interrupt();
		
	}
	
	public static void m1(){
		lock.lock();
		for(int i=0 ;i<20 ; i++){
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				System.out.println("m1() java.lang.InterruptedException");
			}
			System.out.println("m1 *running");
		}
		lock.unlock();
		
	}
	
	public static void m2(){
		try {
			lock2.lockInterruptibly();
			for(int i=0 ;i<20 ; i++){			
				Thread.sleep(500);			
				System.out.println("m2 **running");
			}
		} catch (InterruptedException e) {
			System.out.println("m2() java.lang.InterruptedException");
		}finally{
			if(((ReentrantLock) lock2).isLocked())
				lock2.unlock();
		}
		
	}
}

输出结果:

从输出结果中可以看到,m1()执行,而m2()被打断。

注意:

  • interrupt()方法打断线程休眠状态,会抛出异常  InterruptedException。
  • m1()方法的被打断一次,是因为在调用sleep()方法进入休眠状态时被打断一次。

关于线程中断请看这篇博客:Java并发之线程中断
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值