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

重入锁

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

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()+"释放锁....");
	}
}

 

 

猜你喜欢

转载自zhaodengfeng1989.iteye.com/blog/2412430