一、重入锁
重入锁可以完全代替synchronized
关键字.在JDK5.0的早期版本中,重入锁的性能远远好于synchronized
,但是从JDK6.0开始.JDK在synchronized
上做了大量的优化.使得两者的性能差距不大·
重入锁对于逻辑控制的灵活性要远远好于synchronized
,但是要必须释放锁,否则 其他线程就会一直等待!另外重入锁允许一个线程连续几次获取同一把锁,但是也要释放相同次数。当然如果释放次数多了,也会报错java.lang.IllegalMonitorStateException
二、中断响应
对于synchronized
而言,如果一个线程在等待锁,那么结果只要两种情况: 获得这把锁执行,或者保持等待状态。 而使用重入锁
,则提供了另外一种可能性,那就是线程可以被中断,也就是在等待过程中,程序可以根据需要取消对锁的请求.
2.1 案例分析
下面代码产生了一个死锁,但是得益于锁的中断,我们可以很轻松的解决死锁
package myThread;
import java.util.concurrent.locks.ReentrantLock;
/** * 线程t1和t2启动后,t1占用lock1 再占用lock2 t2先占用lock2 然后请求lock1 因此很容易形成互相等待, * 当我们让t2中断时,他放弃了申请lock1 然后释放了lock2 实际上是 t1线程 完成任务正常退出,而t2 是中断的 */
public class IntLock implements Runnable {
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
public IntLock(int lock) {
this.lock = lock;
}
@Override
public void run() {
try {
if (lock == 1) {
lock1.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock2.lockInterruptibly();
} else {
lock2.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ":线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
IntLock r1 = new IntLock(1);
IntLock r2 = new IntLock(2);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
Thread.sleep(1000);
t2.interrupt();//中断其中一个线程
}
}
运行结果:
12:线程退出
11:线程退出
java.lang.InterruptedException
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 myThread.IntLock.run(IntLock.java:36)
at java.lang.Thread.run(Thread.java:745)
三、锁申请等待限时
锁申请等待限时 除了等待通知之外,要避免死锁还有另外一种方法,那就是限时等待, 就是规定一个时间,超出时间没有拿到锁 就退出
package myThread;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TimeLock implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {//试图获取锁,等待5秒 如果超时那就false
Thread.sleep(6000);
} else {
System.out.println("get lock failed");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) {
TimeLock r1 = new TimeLock();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r1);
t1.start();
t2.start();
}
}
运行结果
get lock failed
由于占用锁的线程会持有锁长达6s,故另一个线程无法在5s的等待时间内获得锁,因此,请求锁会失败!
ReentrantLock.tryLock()
方法可以不带参数直接运行,在这种情况下,当前线程会尝试获得锁,如果锁并未被其他线程占用,则申请会成功!并立即返回true,如果锁被其他线程占用,则当前线程不会执行等待,而是立即返回false。
参考
- 《实战java高并发程序设计》