ReentrantLock代替synchronized
1、ReentrantLock是手工锁,锁住的是ReentrantLock对象,synchronized是自动锁,会自动释放锁,锁住的是堆内存中的对象。synchronized,在代码遇到异常的时候,JVM会自动释放锁。ReentrantLock则不会。
/**
* ReentrantLock替代synchronized
*/
public class ReentrantLock2 {
ReentrantLock lock = new ReentrantLock();
void m1() {
lock.lock(); // 相当于 synchronized
try {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
} finally {
lock.unlock(); // 使用完毕后,必须手动释放锁
// 不同于synchronized,抛出异常后,不会自动释放锁,需要我们在finally中释放此锁
}
}
void m2() {
lock.lock(); // 相当于 synchronized
try {
System.out.println("m2...");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLock2 r1 = new ReentrantLock2();
new Thread(r1::m1, "t1").start(); // m1 已经执行,被t1占有锁this
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(r1::m2, "t2").start(); // 锁已经被其他线程占用,m1执行完毕后,不会执行
}
}
2、ReentrantLock 可以进行尝试锁定 tryLock 这样无法锁定、或者在指定时间内无法锁定,线程可以决定是否继续等待。synchronized则必须一直等待,得不到锁就等一直等待。
public class ReentrantLock3 {
ReentrantLock lock = new ReentrantLock();
void m1() {
lock.lock(); // 相当于 synchronized
try {
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
} finally {
lock.unlock(); // 使用完毕后,必须手动释放锁
// 不同于synchronized,抛出异常后,不会自动释放锁,需要我们在finally中释放此锁
}
}
void m2() {
// 尝试获取锁,返回true拿到了
if (lock.tryLock()) {
// lock.tryLock(5, TimeUnit.SECONDS) // 等5s内还没拿到就返回false
System.out.println("m2...");
} else {
System.out.println(" m2 没拿到锁");
}
lock.unlock();
}
public static void main(String[] args) {
ReentrantLock3 r1 = new ReentrantLock3();
new Thread(r1::m1, "t1").start(); // m1 已经执行,被t1占有锁this
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(r1::m2, "t2").start(); // 锁已经被其他线程占用,m1执行完毕后,不会执行
}
}
3、ReentrantLock 可以调用 lockInterruptibly方法,可以对线程interrupt方法做出响应
/**
* ReentrantLock 和 synchronized 的区别
*
* ReentrantLock 可以调用 lockInterruptibly方法,可以对线程interrupt方法做出响应
* 在一个线程等待锁的过程中,可以被interrupt方法打断等待。
*/
public class ReentrantLock4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
lock.lock();
try {
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); // 线程一直占用锁
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
Thread t2 = new Thread(() -> {
try {
lock.lockInterruptibly(); // t2 尝试获取锁
System.out.println("t2 start");
} catch (InterruptedException e) {
System.out.println("t2 等待中被打断");
} finally {
lock.unlock(); // 没有锁定进行unlock就会抛出 IllegalMonitorStateException
}
}, "t2");
t2.start();
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打断线程2的等待
t2.interrupt();
}
}
4、公平锁
/**
* ReentrantLock 和 synchronized 的区别
* <p>
* ReentrantLock 可以指定为公平锁,synchronized 是不公平锁
* 公平锁,先获取锁的人,在锁被释放时,优先获得锁
* 不公平锁,无论先后,线程调度器将会随机给某个线程锁,不用计算线程时序,效率较高
*/
public class ReentrantLock5 extends Thread {
private static ReentrantLock lock = new ReentrantLock(true);// 指定锁为公平锁
@Override
public void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取锁");
} finally {
lock.unlock(); // 公平锁 t1 unlock 后,等待时间长的一定是 t2 所以下次一定是 t2 执行
}
}
}
public static void main(String[] args) {
ReentrantLock5 r1 = new ReentrantLock5();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r1);
t1.start();
t2.start();
}
}