Lock锁方式解决线程安全问题
概况
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
举个例子
在前面的文章里用synchronized解决了多窗口卖票的线程安全问题:https://blog.csdn.net/qq_41117896/article/details/109473648
这里用Lock锁方式解决
未加措施的原代码如下:
/*
创建三个窗口卖票,总票数为100张,使用实现Runnable接口方式
存在线程的安全问题,后面解决
*/
//1、创建一个实现Runnable接口的子类
class Window implements Runnable{
private int ticket=100;
//2、子类中重写run方法,写任务代码
@Override
public void run() {
while(true){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
public class Tickets {
public static void main(String[] args) {
//3、创建子类的对象
Window w=new Window();
//4、通过Thread类创建线程对象,并将该子类对象作为构造器的参数进行传递
Thread w1=new Thread(w);
Thread w2=new Thread(w);
Thread w3=new Thread(w);
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
//5、调用Thread类的start方法
w1.start();
w2.start();
w3.start();
}
}
使用Lock步骤:
1、实例化ReentrantLock
2、调用锁定方法lock()
3、调用解锁方法
实现如下:
class Windows implements Runnable{
private int ticket=100;
//1、实例化ReentrantLock
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while(true){
try{
//2、调用锁定方法lock()
lock.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);
ticket--;
}else {
break;
}
}finally {
//3、调用解锁方法
lock.unlock();
}
}
}
}
运行结果如下:
没有重票,错票等问题,线程安全问题解决
小练习
银行有一个账户。有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
避免线程安全问题。
分析
两个储户线程
共享数据是账户
存在线程安全问题
考虑同步机制,有三种方式
实现如下:
/*银行有一个账户。
有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
*/
import java.util.concurrent.locks.ReentrantLock;
class Customer implements Runnable{
private double balance;
//1、实例化ReentrantLock
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
try{
//2、调用锁定方法lock()
lock.lock();
for(int i=0;i<3;i++) {
balance += 1000;
System.out.println(Thread.currentThread().getName() + "存钱成功。余额为:" + balance);
}
}finally {
//3、调用解锁方法
lock.unlock();
}
}
}
public class ExerLock {
public static void main(String[] args) {
Customer acct=new Customer();
Thread c1=new Thread(acct);
Thread c2=new Thread(acct);
c1.setName("A");
c2.setName("B");
c1.start();
c2.start();
}
}
结果如下:
synchronized 与 lock 的比较
synchornized是隐式锁,在执行完相应的同步代码块后,自动释放同步监视器
Lock是显式锁,需要手动启动同步(lock()),结束同步也需要手动实现(unlock())
优先使用顺序
Lock -->同步代码块(已经进入了方法体,分配了相应资源) -->同步方法(在方法体之外)