【高并发系列】9、可重入锁那些事儿

重入锁使用java.util.concurrent.locks.ReentrantLock实现;

重入锁与synchronized关键字相比需要显示声明操作过程,对逻辑控制的灵活要远远优于synchronized关键字;

简单例子:

public class ReentrantLockDemo implements Runnable {
	private static ReentrantLock lock = new ReentrantLock();
	private static int i = 0;
	@Override
	public void run() {
		for(int j = 0; j < 10000000; j++) {
			lock.lock();
			try {
				i++;
			} finally {
				lock.unlock();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		ReentrantLockDemo rld = new ReentrantLockDemo();
		Thread t1 = new Thread(rld), t2 = new Thread(rld);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(i);
	}
}

1、中断响应 - lockInterruptibly()

线程等待锁情况
synchronized关键字 重入锁ReentrantLock
获得锁继续执行 获得锁继续执行
保持等待

保持等待

- 中断线程,取消对锁的请求

人为制造一个死锁,并使用可中断机制解决这个死锁:

public class DeadLock implements Runnable {
	private static ReentrantLock lock1 = new ReentrantLock();
	private static ReentrantLock lock2 = new ReentrantLock();
	private int lock;
	public DeadLock(int lock) {
		this.lock = lock;
	}
	@Override
	public void run() {
		try {
			if (this.lock == 1) {
				lock1.lockInterruptibly();
				System.out.println(Thread.currentThread().getName() + ":获取到Lock1锁");
				try {
					TimeUnit.MILLISECONDS.sleep(500);
				} catch (InterruptedException e) {
				}
				lock2.lockInterruptibly();
				System.out.println(Thread.currentThread().getName() + ":获取到Lock2锁");
				System.out.println(Thread.currentThread().getName() + ":执行完成");
			} else {
				lock2.lockInterruptibly();
				System.out.println(Thread.currentThread().getName() + ":获取到Lock2锁");
				try {
					TimeUnit.MILLISECONDS.sleep(500);
				} catch (InterruptedException e) {
				}
				lock1.lockInterruptibly();
				System.out.println(Thread.currentThread().getName() + ":获取到Lock1锁");
				System.out.println(Thread.currentThread().getName() + ":执行完成");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			if (lock1.isHeldByCurrentThread()) {
				lock1.unlock();
				System.out.println(Thread.currentThread().getName() + ":释放Lock1锁");
			}
			if (lock2.isHeldByCurrentThread()) {
				lock2.unlock();
				System.out.println(Thread.currentThread().getName() + ":释放Lock2锁");
			}
			System.out.println(Thread.currentThread().getName() + ":线程退出");
		}
	}
	public static void main(String[] args) throws InterruptedException {
		DeadLock dl1 = new DeadLock(1);
		DeadLock dl2 = new DeadLock(2);
		Thread t1 = new Thread(dl1, "DeadLock1");
		Thread t2 = new Thread(dl2, "DeadLock2");
		t1.start();
		t2.start();
		TimeUnit.SECONDS.sleep(1);
		t2.interrupt();
	}
}

控制台输出:

DeadLock2:获取到Lock2锁
DeadLock1:获取到Lock1锁
java.lang.InterruptedException
DeadLock2:释放Lock2锁
DeadLock1:获取到Lock2锁
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
	at com.freedom.chapter03.DeadLock.run(DeadLock.java:33)
	at java.lang.Thread.run(Thread.java:748)
DeadLock2:线程退出
DeadLock1:执行完成
DeadLock1:释放Lock1锁
DeadLock1:释放Lock2锁
DeadLock1:线程退出

中断后2个线程都退出,但只有DeadLock1真正完成工作,而DeadLock2则放弃任务退出,释放资源;

2、锁申请等待限时 - tryLock()

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
public boolean tryLock()
public class TryLockDemo implements Runnable {
	private ReentrantLock lock = new ReentrantLock();
	@Override
	public void run() {
		try {
			if(lock.tryLock(5, TimeUnit.SECONDS)) {
				System.out.println(Thread.currentThread().getName() + " get the lock.");
				TimeUnit.SECONDS.sleep(6);
			} else {
				System.out.println(Thread.currentThread().getName() + " get the lock failed!");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			if(lock.isHeldByCurrentThread()) {
				lock.unlock();
				System.out.println(Thread.currentThread().getName() + " release the lock!");
			}
		}
	}
	public static void main(String[] args) {
		TryLockDemo tld = new TryLockDemo();
		Thread t1 = new Thread(tld, "TryLockDemo1");
		Thread t2 = new Thread(tld, "TryLockDemo2");
		t1.start();
		t2.start();
	}
}

控制台输出:

TryLockDemo2 get the lock.
TryLockDemo1 get the lock failed!
TryLockDemo2 release the lock!

tryLock()方法不带参数,当前线程尝试获得锁,如果锁并未被其他线程占用,则申请锁成功,立即返回true;如果锁被其他线程占用,则当前线程不会进行等待,而是立即返回false;不会产生死锁:

public class TryLock implements Runnable {
	private static ReentrantLock lock1 = new ReentrantLock();
	private static ReentrantLock lock2 = new ReentrantLock();
	private int lock;
	public TryLock(int lock) {
		this.lock = lock;
	}
	@Override
	public void run() {
		if (lock == 1) {
			while (true) {
				if (lock1.tryLock()) {
					try {
						try {
							TimeUnit.MILLISECONDS.sleep(500);
						} catch (InterruptedException e) {
						}
						if (lock2.tryLock()) {
							try {
								System.out.println(Thread.currentThread().getName() + ":job done.");
								return;
							} finally {
								lock2.unlock();
							}
						}
					} finally {
						lock1.unlock();
					}
				}
			}
		} else {
			while (true) {
				if (lock2.tryLock()) {
					try {
						try {
							TimeUnit.MILLISECONDS.sleep(500);
						} catch (InterruptedException e) {
						}
						if (lock1.tryLock()) {
							try {
								System.out.println(Thread.currentThread().getName() + ":job done.");
								return;
							} finally {
								lock1.unlock();
							}
						}
					} finally {
						lock2.unlock();
					}
				}
			}
		}
	}
	public static void main(String[] args) {
		TryLock tl1 = new TryLock(1);
		TryLock tl2 = new TryLock(2);
		Thread t1 = new Thread(tl1, "TryLockThread1");
		Thread t2 = new Thread(tl2, "TryLockThread2");
		t1.start();
		t2.start();
	}
}

TryLockThread1:job done.
TryLockThread2:job done.

3、公平锁

非公平锁:多个线程请求同一个锁,当该锁可用时,系统只是从该锁的等待队列中随机挑选一个,不能保证公平性;

公平锁:按照时间先后顺序,保证先到先得,后到后得,不会产生饥饿现象;

使用synchronized关键字进行锁控制,产生的锁是非公平锁;

而重入锁允许对公平性进行设置;

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

公平锁要求系统维护一个有序队列,因此实现成本较高,但性能却低下,因此默认情况下,锁是非公平的;

公平锁t1、t2交替打印获取锁,而非公平锁会出现同一个线程连续多次获得锁;

public class FairLockDemo implements Runnable {
//	private static ReentrantLock fairLock = new ReentrantLock(true);
	private static ReentrantLock fairLock = new ReentrantLock();
	@Override
	public void run() {
		while(true) {
			try {
				fairLock.lock();
				System.out.println(Thread.currentThread().getName() + " get the fair lock.");
				try {
					TimeUnit.MILLISECONDS.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} finally {
				fairLock.unlock();
			}
		}
	}
	public static void main(String[] args) {
		FairLockDemo fld = new FairLockDemo();
		Thread t1 = new Thread(fld, "t1");
		Thread t2 = new Thread(fld, "t2");
		t1.start();
		t2.start();
	}
}

根据系统调度,一个线程会倾向于再次获取已经持有的锁,这种分配方式是高效的,但是无公平性可言;

猜你喜欢

转载自blog.csdn.net/hellboy0621/article/details/86891221