欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java多线程高并发篇(一)--重入锁

程序员文章站 2022-06-11 09:32:33
...

重入锁

为什么叫重入锁?是因为同一个线程可以重复进入自己获取的锁.

1.重入锁的特性

1.1重入锁比synchronized更灵活,它能够显式指定何时加锁,何时解锁.(实例方法lock(),unlock())

1.2中断响应

使用ReentrantLock的实例方法lockInterruptibly()方法(建议使用可以响应中断的锁),可以在线程要求中断的时候进行响应,避免死锁情况下一直等待.

 

public class ReentrantLockInterruptDemo implements Runnable {
	public static ReentrantLock lock1 = new ReentrantLock();
	public static ReentrantLock lock2 = new ReentrantLock();
	int i;
	public ReentrantLockInterruptDemo(int i){
		this.i = i;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			if(i==1){
				//对锁的请求,统一使用lockInterruptibly方法,这是一个可以对中断进行响应的锁申请动作.在等待锁的时候可以响应中断
				lock1.lockInterruptibly();
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					// TODO: handle exception
				}
				lock2.lockInterruptibly();
			}else{
				//对锁的请求,统一使用lockInterruptibly方法,这是一个可以对中断进行响应的锁申请动作.在等待锁的时候可以响应中断
				lock2.lockInterruptibly();
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					// TODO: handle exception
				}
				lock1.lockInterruptibly();
			}
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally{
			if(lock1.isHeldByCurrentThread()){
				lock1.unlock();
			}else if(lock2.isHeldByCurrentThread()){
				lock2.unlock();
			}
			System.out.println(Thread.currentThread().getId()+":线程退出");
		}
	}

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Thread t1 = new Thread(new ReentrantLockInterruptDemo(1));
		Thread t2 = new Thread(new ReentrantLockInterruptDemo(2));
		t1.start();
		t2.start();
		Thread.sleep(2000);
		t2.interrupt();
	}

}

 

1.3锁申请等待限时

申请锁的时候(使用tryLock(timeout, unit)方法),可以指定等待时间,如果在指定时间内没有获取到锁,返回false,否则返回true;而无参的tryLock()方法,当前线程会去尝试获得锁,如果锁被其他线程占用,则立即返回false,不会引起线程等待.

 

public class ReentrantLockTryLockDemo implements Runnable {
	public static ReentrantLock lock = new ReentrantLock();
	
	@Override
	public void run() {
		try {
			System.out.println("我进来了,我要获取锁,我是_"+Thread.currentThread().getName());
			if(lock.tryLock(5, TimeUnit.SECONDS)){
				System.out.println("谁进来了?我是_"+Thread.currentThread().getName());
				Thread.sleep(6000);
			}else{
				System.out.println("没获取到锁,我走了_"+Thread.currentThread().getName());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(lock.isHeldByCurrentThread()){
				lock.unlock();
			}
		}
	}

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		ReentrantLockTryLockDemo rt = new ReentrantLockTryLockDemo();
		Thread t1 = new Thread(rt);
		Thread t2 = new Thread(rt);
		t1.start();
		t2.start();
	}

}

 

1.4公平锁设置

重入锁可以对锁的公平性进行设置.在ReentrantLock的构造函数中.公平锁需要维护一个队列(按照线程的进入顺序,不论优先级高低).

但是由于实现成本较高,性能比较低,因此默认情况下是非公平的.

public class ReentrantLockFairDemo implements Runnable {
	//设置公平锁
	public static ReentrantLock fairLock = new ReentrantLock(true);
	
	@Override
	public void run() {
		while(true){
			try {
				fairLock.lock();
				System.out.println("我是_"+Thread.currentThread().getName());
			} catch (Exception e) {
				e.printStackTrace();
			}finally{
				fairLock.unlock();
			}
		}
	}

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		ReentrantLockFairDemo rt = new ReentrantLockFairDemo();
		Thread t1 = new Thread(rt,"Thread_t1");
		Thread t2 = new Thread(rt,"Thread_t2");
		t1.start();
		t2.start();
	}

 

 

1.5重入锁的好伙伴:Condition条件对象

在synchronized中,我们可以使用Object对象提供的wait()和notify()方法实现线程的挂起和唤醒(线程Thread中的suspend和resume已经废弃).

换句话说,Object对象中的wait()和notify()方法是和synchronized合用起来完成线程的挂起和唤醒操作的.

在重入锁中,我们使用Condition条件对象与重入锁合用,来实现线程的挂起(await)与唤醒(singal).

在使用时,我们需要使用ReentrantLock中的实例方法newCondition()得到Condition对象,将重入锁与对应的Condition对象进行绑定,实现线程的等待与唤醒.

具体方法介绍如下:

await()方法:与Object类中的wait()方法类似,在Condition接口中,提供了方法await(),调用后使当前线程等待,同时释放当前锁.在等待过程中可以响应中断.

awaitUninterruptibly()方法:功能与await()一样,不同的是,在等待过程中不响应中断.

singal()方法:用于唤醒一个在等待队列中的线程.

singalAll()方法:用于唤醒所有在等待队列中的线程.

 

public class ReentrantLockCondition implements Runnable{
	public static ReentrantLock lock = new ReentrantLock();
	public static Condition condition = lock.newCondition();
	
	@Override
	public void run() {
		try {
			//加锁
			lock.lock();
			System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"获取锁执行等待....");
			//等待,并释放锁
			condition.await();
			System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"开始执行任务....");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"执行完成....");
			lock.unlock();
			System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"线程释放锁....");
		}
	}
	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Thread t1 = new Thread(new ReentrantLockCondition());
		Thread t2 = new Thread(new ReentrantLockCondition());
		t1.start();
		t2.start();
		Thread.sleep(2000);
		System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"线程获取锁....");
		//重新获取锁
		lock.lock();
		//唤醒
		System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"线程通知T1....");
		condition.signalAll();
		//释放锁
		lock.unlock();
		System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"释放锁....");
	}
}

 

 

相关标签: 高并发