java多线程之Lock锁原理以及案例实现电影院卖票

为什么会出现Lock锁?

我们知道 synchronized 给代码加锁或解锁时,我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock锁使用

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

Lock中提供了获得锁和释放锁的方法:

  • void lock();//获得锁
  • void unlock();//释放锁
  • Lock类是接口,不能直接实例化,这里采用他的实现类ReentrantLock来实例化
    ReentrantLock的构造方法
    ReentrantLock():创建一个ReentrantLock的实例

代码实战

需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

java 复制代码
package com.heima.thread001;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyRun implements Runnable {
    int ticket = 0;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();
            try {
                if (ticket == 100) {
                    break;
                }else {
                    ticket ++;
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票 ...");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}
java 复制代码
package com.heima.thread001;

import java.util.concurrent.ExecutionException;

public class TestDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        MyRun myRun = new MyRun();
        //创建线程对象
        Thread t1 = new Thread(myRun);
        Thread t2 = new Thread(myRun);
        Thread t3 = new Thread(myRun);

        //起名字
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        //开启线程
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果如下:

java 复制代码
窗口1正在卖第1张票 ...
窗口3正在卖第2张票 ...
窗口3正在卖第3张票 ...
窗口3正在卖第4张票 ...
窗口3正在卖第5张票 ...
窗口3正在卖第6张票 ...
窗口3正在卖第7张票 ...
窗口3正在卖第8张票 ...
窗口3正在卖第9张票 ...
窗口3正在卖第10张票 ...
窗口3正在卖第11张票 ...
窗口3正在卖第12张票 ...
窗口3正在卖第13张票 ...
窗口3正在卖第14张票 ...
窗口3正在卖第15张票 ...
窗口3正在卖第16张票 ...
窗口3正在卖第17张票 ...
窗口3正在卖第18张票 ...
窗口3正在卖第19张票 ...
窗口3正在卖第20张票 ...
窗口3正在卖第21张票 ...
窗口3正在卖第22张票 ...
窗口3正在卖第23张票 ...
窗口3正在卖第24张票 ...
窗口3正在卖第25张票 ...
窗口3正在卖第26张票 ...
窗口3正在卖第27张票 ...
窗口3正在卖第28张票 ...
窗口3正在卖第29张票 ...
窗口3正在卖第30张票 ...
窗口3正在卖第31张票 ...
窗口3正在卖第32张票 ...
窗口3正在卖第33张票 ...
窗口3正在卖第34张票 ...
窗口3正在卖第35张票 ...
窗口3正在卖第36张票 ...
窗口3正在卖第37张票 ...
窗口3正在卖第38张票 ...
窗口3正在卖第39张票 ...
窗口3正在卖第40张票 ...
窗口3正在卖第41张票 ...
窗口3正在卖第42张票 ...
窗口3正在卖第43张票 ...
窗口3正在卖第44张票 ...
窗口3正在卖第45张票 ...
窗口3正在卖第46张票 ...
窗口3正在卖第47张票 ...
窗口3正在卖第48张票 ...
窗口3正在卖第49张票 ...
窗口3正在卖第50张票 ...
窗口3正在卖第51张票 ...
窗口3正在卖第52张票 ...
窗口3正在卖第53张票 ...
窗口3正在卖第54张票 ...
窗口3正在卖第55张票 ...
窗口3正在卖第56张票 ...
窗口3正在卖第57张票 ...
窗口3正在卖第58张票 ...
窗口3正在卖第59张票 ...
窗口3正在卖第60张票 ...
窗口3正在卖第61张票 ...
窗口3正在卖第62张票 ...
窗口3正在卖第63张票 ...
窗口3正在卖第64张票 ...
窗口3正在卖第65张票 ...
窗口3正在卖第66张票 ...
窗口3正在卖第67张票 ...
窗口3正在卖第68张票 ...
窗口3正在卖第69张票 ...
窗口3正在卖第70张票 ...
窗口3正在卖第71张票 ...
窗口3正在卖第72张票 ...
窗口3正在卖第73张票 ...
窗口3正在卖第74张票 ...
窗口3正在卖第75张票 ...
窗口3正在卖第76张票 ...
窗口3正在卖第77张票 ...
窗口3正在卖第78张票 ...
窗口3正在卖第79张票 ...
窗口3正在卖第80张票 ...
窗口3正在卖第81张票 ...
窗口3正在卖第82张票 ...
窗口3正在卖第83张票 ...
窗口3正在卖第84张票 ...
窗口3正在卖第85张票 ...
窗口3正在卖第86张票 ...
窗口3正在卖第87张票 ...
窗口3正在卖第88张票 ...
窗口3正在卖第89张票 ...
窗口3正在卖第90张票 ...
窗口3正在卖第91张票 ...
窗口3正在卖第92张票 ...
窗口3正在卖第93张票 ...
窗口3正在卖第94张票 ...
窗口3正在卖第95张票 ...
窗口3正在卖第96张票 ...
窗口3正在卖第97张票 ...
窗口3正在卖第98张票 ...
窗口3正在卖第99张票 ...
窗口3正在卖第100张票 ...

知识扩展

1、synchronized和Lock的区别?

  • synchronized是关键字,Lock是接口。
  • synchronized无法获取锁的状态,Lock可以。
  • synchronized会自动释放锁,Lock需要手动。
  • synchronized没有Lock锁灵活(Lock锁可以自己定制)。

2、ReentrantLock 和 Synchronized 对比

  • ReentrantLock 通过 lock() 和 unlock() 加解锁,Synchronized会自动解锁,ReentrantLock需要手动解锁
  • ReentrantLock相比Synchronized的优势是: 可中断,公平锁,多个锁

3、Lock 接口的主要方法

  • void lock() --- 如果lock是空闲的,当前线程就会获取到锁
  • boolean tryLock() --- 如果lock可用,则获取锁并且返回true,否则返回false
  • void unlock() --- 当前线程释放持有的锁
  • Condition newCondition() --- 条件对象: 获取等待通知组件
  • getHoldCount() --- 查询当前线程保持此锁的次数
  • getQueueLength() --- 返回正在等待此锁的线程数量
  • getWaitQueueLength() --- 返回正在等待与此锁相关给定条件的线程数量
  • hasWaiters(Condition condition) --- 查询在特定Condition下是否有线程等待
  • hasQueuedThread(Thread thread) --- 查询给定线程是否等待获取此锁
  • hasQueuedThreads --- 是否有线程等待此锁
  • isFair() --- 该锁是否是公平锁
  • isHeldByCurrentThread() --- 当前线程是否保持锁定
  • isLock() --- 此锁是否有任意线程占用
  • lockInterruptibly() --- 如果当前线程未被中断,获取锁
  • tryLock() --- 尝试获取锁,仅在调用时锁未被线程占用,获得锁
  • tryLock(long timeout, TimeUnit unit) --- 如果锁在给定等待时间没有被另一个线程保持,获取锁

锁优化

  • 减少锁持有的时间 --- 只在有线程安全要求的程序上加锁
  • 减少锁粒度 --- 将大的对象拆成小对象,增加并行度,减少锁竞争
  • 锁分离 --- 读写锁的思想,读的时候加read lock, 写的时候加 write lock
  • 锁粗化 --- 保证线程持有锁的时间尽量短
  • 锁消除 --- 如果发现不能被共享的对象,需要消除这些对象的锁操作
相关推荐
公贵买其鹿6 分钟前
List深拷贝后,数据还是被串改
java
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹4 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫5 小时前
泛型(2)
java
超爱吃士力架5 小时前
邀请逻辑
java·linux·后端
南宫生5 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石5 小时前
12/21java基础
java
李小白665 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp5 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea