文章目录
1. ReentrantLock概述
可重入性描述这样的一个问题:一个线程在持有一个锁的时候,它内部能否再次(多次)申请该锁。如果一个线程已经获得了锁,其内部还可以多次申请该锁成功。
void methodA(){
lock.lock(); // 获取锁
methodB();
lock.unlock() // 释放锁
}
void methodB(){
lock.lock(); // 获取锁
// 其他业务
lock.unlock();// 释放锁
}
2. Lock接口,ReentrantLock说明
Modifier and Type | Method | Description |
---|---|---|
void | lock() | 获取锁 |
void | lockInterruptibly() | 除非当前线程被中断,否则获取锁定 |
Condition | newCondition() | 返回绑定到此Lock实例的新Condition实例 |
boolean | tryLock() | 只有在调用时它是空闲的才能获取锁 |
boolean | tryLock(long time, TimeUnit unit) | 如果在给定的等待时间内空闲并且当前线程未被中断,则获取锁 |
void | unlock() | 释放锁 |
2.1 lock 和unlock方法说明
下面的事例模拟售票的场景, tickets是总票数,开启了10个售票窗口,售完为止:
public class ReentrantLockDemo01 implements Runnable {
private Lock lock = new ReentrantLock();
private int tickets = 200;
@Override
public void run() {
while (true) {
lock.lock(); // 获取锁
try {
if (tickets > 0) {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread().getName() + " " + tickets--);
} else {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放所
}
}
}
public static void main(String[] args) {
ReentrantLockDemo01 reentrantLockDemo = new ReentrantLockDemo01();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(reentrantLockDemo, "thread" + i);
thread.start();
}
}
}
2.2 LockInterruptibly 方法
- 使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应
- 在一个线程等到的过程中,可以被打断
public class ReentrantLock3 {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
try {
lock.lock();
System.out.println("t1 start........");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end..........");
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println("interrupted......");
} finally {
lock.unlock();
}
});
t1.start();
Thread t2 = new Thread(() -> {
try {
// lock.lock();
lock.lockInterruptibly(); //可以对interrupted方法做出响应
System.out.println("t2 start........");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end...........");
} catch (Exception e) {
// TODO: handle exception
System.out.println("interrupted.......");
} finally {
if(lock.tryLock()) {
lock.unlock();
System.out.println("unlock....");
}
// lock.unlock();//,t2的finally代码块中的lock.unlock()会报异常,原因在于lock根本没有上锁
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
t2.interrupt();//打断线程2 的等待,但是t1仍会继续执行
}
}
2.3 tryLock 方法
- 使用tryLock来尝试上锁,上锁成功则返回true,否则返回false
public class ReentrantLock4 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void m2() {
// boolean locked = lock.tryLock();
// System.out.println("m2......." + locked);
// if(locked) lock.unlock();
boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2......." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if(locked)
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLock4 r1 = new ReentrantLock4();
new Thread(r1::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
new Thread(r1::m2).start();
}
}
2.4 锁的手动释放
- 需要注意的是,使用reentrantLock必须手工释放锁
- 使用synchronized锁如果出现异常的话,jvm会自动释放锁,但是lock必须手工释放,可以在finally代码块中释放
public class ReentrantLock2 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
void m2() {
lock.lock();
System.out.println("m2............");
lock.unlock();
}
public static void main(String[] args) {
ReentrantLock2 r1 = new ReentrantLock2();
new Thread(r1::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
new Thread(r1::m2).start();
}
}
2.5 公平锁
- ReentrantLock可以指定为公平锁,默认的synchronized是非公平锁
- 公平锁是谁等的时间长,锁给谁
public class ReentrantLock5 extends Thread{
private static ReentrantLock lock = new ReentrantLock(true);//参数为true表明其为公平锁
public void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "get the lock");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentrantLock5 r1 = new ReentrantLock5();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r1);
t1.start();
t2.start();
}
}
2.6 newCondition() 方法说明
- Condition的作用是对锁进行更精确的控制。
- Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。
- 不同的是,Object中的wait(),notify(),notifyAll()方法是和”同步锁”(synchronized关键字)捆绑使用的;而Condition是需要与”互斥锁”/”共享锁”捆绑使用的。
这段代码还有些问题???
public class ProducerConsumerTest {
private Lock lock = new ReentrantLock();
private Condition addCondition = lock.newCondition();
private Condition removeCondition = lock.newCondition();
private LinkedList<Integer> resources = new LinkedList<>();
private int maxSize;
public ProducerConsumerTest(int maxSize) {
this.maxSize = maxSize;
}
public class Producer implements Runnable {
private int proSize;
private Producer(int proSize) {
this.proSize = proSize;
}
@Override
public void run() {
lock.lock();
try {
for (int i = 1; i < proSize; i++) {
while (resources.size() >= maxSize) {
System.out.println("当前仓库已满,等待消费...");
try {
addCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("已经生产产品数: " + i + "\t现仓储量总量:" + resources.size());
resources.add(i);
removeCondition.signal();
}
} finally {
lock.unlock();
}
}
}
public class Consumer implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (true) {
lock.lock();
try {
while (resources.size() <= 0) {
System.out.println(threadName + " 当前仓库没有产品,请稍等...");
try {
// 进入阻塞状态
removeCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费数据
int size = resources.size();
for (int i = 0; i < size; i++) {
Integer remove = resources.remove();
System.out.println(threadName + " 当前消费产品编号为:" + remove);
}
// 唤醒生产者
addCondition.signal();
} finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
ProducerConsumerTest producerConsumerTest = new ProducerConsumerTest(10);
Producer producer = producerConsumerTest.new Producer(100);
Consumer consumer = producerConsumerTest.new Consumer();
final Thread producerThread = new Thread(producer, "producer");
final Thread consumerThread = new Thread(consumer, "consumer");
producerThread.start();
TimeUnit.SECONDS.sleep(2);
consumerThread.start();
}
}