03、Java并发编程 - Lock锁详解
3、Lock锁
3.1. 多线程的编程模型
多线程编程的代码编写模型:
1、高内聚,低耦合 :必须保证业务代码的高内聚、低耦合;
2、线程去操作(调用对外暴露的方法) 资源类。
3.2. 使用传统的关键字synchronized实现Lock锁
要按照多线程的编程模型来实现Lock锁。如下方式,在企业中静止这样写。
package com.interview.concurrent.lock;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @description 描述
* @date 2023/2/21 11:15
*/
public class SaleTicketUseSynchronizeedError {
public static void main(String[] args) {
//1、错误的写法
SaleTicketThreadError saleTicketThreadError = new SaleTicketThreadError();
for (int i=0;i<30;i++) new Thread(saleTicketThreadError,"A").start();
for (int i=0;i<30;i++) new Thread(saleTicketThreadError,"B").start();
for (int i=0;i<30;i++) new Thread(saleTicketThreadError,"C").start();
for (int i=0;i<30;i++) new Thread(saleTicketThreadError,"D").start();
for (int i=0;i<30;i++) new Thread(saleTicketThreadError,"E").start();
}
}
/**
* @description: 错误的写法,不能让资源和方法高度封装,达不到高内聚
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @date 2023/2/20 19:45
*/
class SaleTicketThreadError implements Runnable{
int ticketNumber = 50;
@Override
public synchronized void run() {
if(ticketNumber 0)
System.out.println(Thread.currentThread().getName() + "卖出第" + (ticketNumber--) + "张票,还剩" + ticketNumber + "张票");
}
}
正确的写法是:将方法和资源单独封装,不要和线程有任何关系(线程是通过方法去操作资源的)。
package com.interview.concurrent.lock;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @description 描述:卖票,5个售票员卖出50张票
* @date 2023/2/20 19:29
*/
public class SaleTicketUseSynchronizeed {
public static void main(String[] args) {
//2、正确的写法,将资源和方法高度封装,达到高内聚
SaleTicket saleTicket = new SaleTicket();
new Thread(()-{
for (int i=0;i<20;i++) saleTicket.saleTicket();
},"A").start();
new Thread(()-{
for (int i=0;i<20;i++) saleTicket.saleTicket();
},"B").start();
new Thread(()-{
for (int i=0;i<20;i++) saleTicket.saleTicket();
},"C").start();
new Thread(()-{
for (int i=0;i<20;i++) saleTicket.saleTicket();
},"D").start();
new Thread(()-{
for (int i=0;i<20;i++) saleTicket.saleTicket();
},"E").start();
}
}
/**
* @description: 资源类:将属性(票)和方法(卖票)封装起来,实现了高内聚
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @date 2023/2/20 19:42
*/
class SaleTicket{
int ticketNumber = 50;
//卖票方法
public synchronized void saleTicket(){
if(ticketNumber 0)
System.out.println(Thread.currentThread().getName() + "卖出第" + (ticketNumber--) + "张票,还剩" + ticketNumber + "张票");
}
}
3.3. 使用juc.locks包下的类实现Lock锁
3.3.1. Lock的编程模型
使用 juc.locks 包下的类操作 Lock 锁 + Lambda 表达式。
Lock是一个接口,其实现类ReentranLock(可重入锁,后面章节会详细讲解)。
Lock的编程模型:
1、创建锁;
2、加锁:lock.lock();
3、在try/catch里面写业务代码;
4、在try/catch的finally里面解锁lock.unlock();
注意:Lock的加锁解锁要配对出现,即有一个lock.lock()加锁,就必须在finally里面有一个lock.unlock(),出现两个lock.lock()加锁,在finally里面也必须有两个lock.unlock(),
3.3.2. 示例代码
完整代码如下:
package com.interview.concurrent.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @description 描述
* @date 2023/2/21 11:41
*/
public class SaleTicketUseLock {
public static void main(String[] args) {
SaleTicketLock saleTicketLock = new SaleTicketLock();
new Thread(()-{
for(int i=0;i<20;i++) saleTicketLock.saleTicket();},"A").start();
new Thread(()-{
for(int i=0;i<20;i++) saleTicketLock.saleTicket();},"B").start();
new Thread(()-{
for(int i=0;i<20;i++) saleTicketLock.saleTicket();},"C").start();
new Thread(()-{
for(int i=0;i<20;i++) saleTicketLock.saleTicket();},"D").start();
new Thread(()-{
for(int i=0;i<20;i++) saleTicketLock.saleTicket();},"E").start();
}
}
class SaleTicketLock{
int ticketNumber = 50;
//1、创建锁
Lock lock = new ReentrantLock();
public void saleTicket(){
//2、加锁
lock.lock();
try{
//3、业务代码
if(ticketNumber 0)
System.out.println(Thread.currentThread().getName() + "卖出第" + (ticketNumber--) + "张票,还剩" + ticketNumber + "张票");
}catch(Exception e) {
e.printStackTrace();
}finally {
//4、解锁
lock.unlock();
}
}
}
3.3. synchronized 和 Lock 区别
synchronized 和 lock 区别 (自动挡 手动挡)。
1、synchronized 关键字,Java内置的。Lock 是一个Java 类;
2、synchronized 无法判断是否获取锁。Lock可以判断是否获得锁(通过isLocked()方法);
3、synchronized 锁会自动释放! Lock需要手动在 finally 释放锁,如果不释放锁,就会死锁,Lock的加锁解锁要配对出现;
4、synchronized线程1阻塞,线程2永久等待下去。 Lock可以 lock.tryLock(); // 尝试获取锁,如果尝试获取不到锁,可以结束等待;
5、synchronized 可重入,不可中断,非公平的。Lock锁,可重入、可以判断、可以公平!
公平:线程必须排队,必须先来后到;
非公平:线程可以插队。
出现;
4、synchronized线程1阻塞,线程2永久等待下去。 Lock可以 lock.tryLock(); // 尝试获取锁,如果尝试获取不到锁,可以结束等待;
5、synchronized 可重入,不可中断,非公平的。Lock锁,可重入、可以判断、可以公平!
公平:线程必须排队,必须先来后到;
非公平:线程可以插队。