synchronized局限性
- 无法中断一个正在获取锁的线程
当一个线程想获取已经被其他线程持有的锁时,就会发生堵塞,假设已经持有锁的线程一直不释放锁,那么线程就会一直等待下去。
- 无法指定获得锁的等待时间
相同点
- 独占锁:一次只允许一个线程访问
- 可重入锁: 一个线程可重复获得自己已获得锁,不会发生死锁。简单来说,递归的时候不会发生死锁
不同点
- Lock不是java内置的,synchronized是JVM内置的,因此是内置特性。
- 释放锁的方式:
- Lock 必须要在finally中手动释放锁
- synchronized会根据锁区域代码自动执行完毕,或者发生异常,JVM会自动释放锁
- 公平: Lock是可公平可不公平锁,synchronized是不公平锁
ReentrantLock
1.中断响应
线程在等待锁的过程中,程序可以根据需要取消对锁的请求。在对锁的请求中,统一使用lockInterruptibly()方法。这是一个可以对中断进行响应的锁请求动作,即在等待锁的过程中,可以响应中断。
public class IntLock implements Runnable {
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
/**
* 控制加锁顺序,方便构造死锁
* @param 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){}
lock2.lockInterruptibly();
} else {
lock2.lockInterruptibly();
try{
Thread.sleep(500);
}catch(InterruptedException e){}
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();
}
}
线程1和12启动后,t1先占用lock1,再占用lock2; 12先占用lok2,再占用lock1.因此,很容易形成t1和12之间的相互等待.在主线程main中处于休眠状态时,这两个线程处于死锁的状态。然后由于t2线程被中断,故t2会放弃对lock1的申请,同时释放已获得的lock2。这个操作导致tl线程可 以顺利得到lock2而继续执行下去。
2.限时等待
public class TimeLock implements Runnable{
public static ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
try {
if(lock.tryLock(5, TimeUnit.SECONDS)){
Thread.sleep(6000);
}else{
System.out.println("get lock failed");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{lock.unlock();}
}
public static void main(String[] args) {
TimeLock tl=new TimeLock();
Thread t1=new Thread(tl);
Thread t2=new Thread(tl);
t1.start();
t2.start();
}
}
当线程无法在指定时间内获得锁后,请求锁失败,当然trylock()方法也可以不带参数运行,在这种情况下,当前线程会尝试获得锁,如果锁并没有被其他锁只能用,则申请锁成功,斗则不会进行等待,立即返回false。
3.公平锁
g公平锁不会产生饥饿现象,只要排队一定可以等到锁,但是需要维护一个有序队列,因此实现成本较高,性能低下。
public class FairLock implements Runnable {
public static ReentrantLock fairLock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
fairLock.lock();
System.out.println(Thread.currentThread().getName()+" 获得锁");
}finally{
fairLock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
FairLock r1 = new FairLock();
Thread t1=new Thread(r1,"Thread_t1");
Thread t2=new Thread(r1,"Thread_t2");
t1.start();t2.start();
}
}