ReentrantLock
相对于synchronized,它具备的特点是:
- 可中断
- 可以设置超时时间
- 可以设置为公平锁
- 支持多个条件变量
与synchronized一样,都支持可重入。
基本语法:
1 2 3 4 5 6 7 8
| reentrantLock.lock(); try{ }finally{ reentrantLock.unlock(); }
|
1、ReentrantLock的特点
1、可重入
可重入是指:同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此由权力再次获取这把锁。
下面这个例子:主线程调用方法method1(),在method1中获取lock锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public static ReentrantLock lock = new ReentrantLock();
public static void method1(){ try{ lock.lock(); System.out.println("方法1 执行"); method2(); }finally { lock.unlock(); } } public static void method2(){ try{ lock.lock(); System.out.println("方法2 执行"); method3(); }finally { lock.unlock(); } } public static void method3(){ try { lock.lock(); System.out.println("方法3 执行"); }finally { lock.unlock(); } }
public static void main(String[] args) { method1(); }
|

2、可打断
lock.lockInterruptibly()
方法。
如果没有竞争那么此方法就会获取lock对象锁。
如果有竞争就进入阻塞队列,可以被其他线程用 interruput 方法打断。
下面这个例子:主线程先获取锁,t1线程进入阻塞队列。主线程执行中断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public static void main(String[] args) throws InterruptedException { ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> { System.out.println("t1线程启动");
try{ lock.lockInterruptibly(); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("等待的过程中被打断"); return; }
try { System.out.println("获得了锁"); }finally { lock.unlock(); }
},"t1");
lock.lock(); System.out.println("主线程获得了锁"); t1.start();
try { sleep(10); t1.interrupt(); System.out.println("执行中断"); }finally { lock.unlock(); } }
|

3、 锁超时
当线程在尝试获取锁时,可能锁已经被其他线程占了。
这是我们可以设置一段时间,在这段时间内,线程可以获得锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public static void main(String[] args) throws InterruptedException { ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> { System.out.println("t1线程启动");
try { if(!lock.tryLock(1, TimeUnit.SECONDS)){ System.out.println("获取锁等待1s后失败,返回"); return; } } catch (InterruptedException e) { e.printStackTrace(); }
try{ System.out.println("t1线程获取锁"); }finally { lock.unlock(); } },"t1");
lock.lock(); System.out.println("主线程获取了锁"); t1.start();
try{ sleep(3000); }finally { System.out.println("主线程释放锁"); lock.unlock(); } }
|

4、公平锁
ReentrantLock 默认是不公平的
1 2 3 4 5 6 7
| public ReentrantLock() { sync = new NonfairSync(); }
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
|
不公平锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public static void main(String[] args) throws InterruptedException { ReentrantLock lock = new ReentrantLock(false);
lock.lock();
for (int i = 0; i < 500; i++) { new Thread(() -> { lock.lock(); try{ System.out.println(Thread.currentThread().getName() + "Running"); }finally { lock.unlock(); } },"t" + i).start(); }
Thread.sleep(1000); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " start"); lock.lock(); try{ System.out.println(Thread.currentThread().getName() + " Running"); }finally { lock.unlock(); } },"强行插入").start();
lock.unlock(); }
|

公平锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
for (int i = 0; i < 500; i++) { new Thread(() -> { lock.lock(); try{ System.out.println(Thread.currentThread().getName() + "Running"); }finally { lock.unlock(); } },"t" + i).start(); }
Thread.sleep(1000); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " start"); lock.lock(); try{ System.out.println(Thread.currentThread().getName() + " Running"); }finally { lock.unlock(); } },"强行插入").start();
lock.unlock();
}
|

条件变量
在synchronized中也有条件变量。
某一个线程获取锁之后,调用wait()方法。就会进入waitiing队列中。(这就是为什么调用wait()方法必须先所得锁)
但是synchronized中只有一个wait队列。而ReentrantLock支持多个wait队列。
如何使用:
- await前需要获得锁
- await执行后,会释放锁,进入conditionObject等待
- await的线程被唤醒(或打断、或超时)去重新竞争lock锁
- 竞争lock锁成功后,从await后继续执行
例子:有两个员工需要等到烟和早餐才能工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| public class test1 {
static ReentrantLock lock = new ReentrantLock(); static Condition waitCigaretteQueue = lock.newCondition(); static Condition waitbreakfastQueue = lock.newCondition(); static volatile boolean hasCigrette = false; static volatile boolean hasBreakfast = false;
public static void main(String[] args) throws InterruptedException { new Thread(() -> { try { lock.lock(); while(!hasCigrette){ try{ waitCigaretteQueue.await(); System.out.println("t1线程正在等烟"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t1线程等到了他的烟"); }finally { lock.unlock(); } },"t1").start();
new Thread(() -> { try { lock.lock(); while (!hasBreakfast) { try { System.out.println("t2线程正在等早餐"); waitbreakfastQueue.await();
} catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t2等到了他的早餐"); } finally { lock.unlock(); } },"t2").start();
sleep(1); sendBreakfast(); sleep(1); sendCigarette();
}
private static void sendCigarette() { lock.lock(); try { System.out.println("t1线程烟到了"); hasCigrette = true; waitCigaretteQueue.signal(); } finally { lock.unlock(); } } private static void sendBreakfast() { lock.lock(); try { System.out.println("t2线程早餐到了"); hasBreakfast = true; waitbreakfastQueue.signal(); } finally { lock.unlock(); } }
}
|