Java 多线程学习笔记二十三(Lock锁)
内容来自B站【狂神说Java】多线程详解
Lock(锁)
- 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显示定义同步锁对象来实现同步,同步锁使用Lock对象充当。
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
- ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。
使用到的地方如:CopyOnWriteArrayList类中final transient ReentrantLock lock = new ReentrantLock();
- 使用
lock.lock()
加锁,lock.unlock()
解锁,如果有异常,需要将lock.unlock()
写在finally
代码块中。
synchronized与Lock对比
- Lock是显示锁(手动开启和关闭锁,别忘记关闭锁);synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁;synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序
- Lock锁 > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)
代码演示
一、未使用Lock锁
package com.example.demo.thread.sync.lock;
public class TestLock {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
for (int i = 0; i < 20; i++) {
new Thread(buyTicket, "用户" + i).start();
}
}
}
class BuyTicket implements Runnable {
private int ticketNums = 10;
@Override
public void run() {
try {
this.buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void buy() throws InterruptedException {
// 模拟查询是否还有票
Thread.sleep(1000);
if (this.ticketNums > 0) {
// 模拟买票耗时
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "买到了第 "+ this.ticketNums +" 张票,还剩 " + (--this.ticketNums) + "张票。");
} else {
System.out.println(Thread.currentThread().getName() + "没有买到票,已售罄。");
}
}
}
输出
用户0买到了第 3 张票,还剩 1张票。
用户7买到了第 0 张票,还剩 -1张票。
用户14买到了第 -7 张票,还剩 -8张票。
用户15买到了第 -6 张票,还剩 -7张票。
用户11买到了第 10 张票,还剩 5张票。
用户6买到了第 10 张票,还剩 8张票。
用户2买到了第 10 张票,还剩 9张票。
用户12买到了第 -3 张票,还剩 -5张票。
用户1买到了第 -3 张票,还剩 -4张票。
用户9买到了第 -5 张票,还剩 -6张票。
用户17买到了第 10 张票,还剩 3张票。
用户16买到了第 0 张票,还剩 -3张票。
用户4买到了第 10 张票,还剩 5张票。
用户18买到了第 10 张票,还剩 9张票。
用户13买到了第 10 张票,还剩 4张票。
用户19买到了第 10 张票,还剩 6张票。
用户8买到了第 1 张票,还剩 0张票。
用户10买到了第 3 张票,还剩 2张票。
用户5买到了第 0 张票,还剩 -2张票。
用户3买到了第 10 张票,还剩 7张票。
二、使用Lock锁
package com.example.demo.thread.sync.lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
for (int i = 0; i < 20; i++) {
new Thread(buyTicket, "用户" + i).start();
}
}
}
class BuyTicket implements Runnable {
private int ticketNums = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
lock.lock();
this.buy();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void buy() throws InterruptedException {
// 模拟查询是否还有票
Thread.sleep(1000);
if (this.ticketNums > 0) {
// 模拟买票耗时
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "买到了第 "+ this.ticketNums +" 张票,还剩 " + (--this.ticketNums) + "张票。");
} else {
System.out.println(Thread.currentThread().getName() + "没有买到票,已售罄。");
}
}
}
输出
用户0买到了第 10 张票,还剩 9张票。
用户1买到了第 9 张票,还剩 8张票。
用户2买到了第 8 张票,还剩 7张票。
用户3买到了第 7 张票,还剩 6张票。
用户4买到了第 6 张票,还剩 5张票。
用户5买到了第 5 张票,还剩 4张票。
用户6买到了第 4 张票,还剩 3张票。
用户7买到了第 3 张票,还剩 2张票。
用户8买到了第 2 张票,还剩 1张票。
用户9买到了第 1 张票,还剩 0张票。
用户10没有买到票,已售罄。
用户11没有买到票,已售罄。
用户12没有买到票,已售罄。
用户13没有买到票,已售罄。
用户14没有买到票,已售罄。
用户15没有买到票,已售罄。
用户16没有买到票,已售罄。
用户17没有买到票,已售罄。
用户18没有买到票,已售罄。
用户19没有买到票,已售罄。