重入锁使用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();
}
}
根据系统调度,一个线程会倾向于再次获取已经持有的锁,这种分配方式是高效的,但是无公平性可言;