JUC学习(二):Lock的介绍及使用(实现多线程卖票)

简介: JUC学习(二):Lock的介绍及使用(实现多线程卖票)

一、Lock简介


     

Lock 锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。Lock 提供了比 synchronized 更多的功能。


Lock 与的 Synchronized 区别:


Lock 不是 Java 语言内置的,synchronized 是 Java 语言的关键字,因此是内置特性。Lock 是一个类,通过这个类可以实现同步访问;


Lock 和 synchronized 有一点非常大的不同,采用 synchronized 不需要用户去手动释放锁,当 synchronized 方法或者 synchronized 代码块执行完之后,系统会自动让线程释放对锁的占用;而 Lock 则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。


二、Lock中的lock()方法


 

lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。


采用 Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用 Lock 必须在 try{}catch{}块中进行,并且将释放锁的操作放在 finally 块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用 Lock 来进行同步的话,是以下面这种形式去使用的:


Lock lock = ...; lock.lock(); try{ 
   //处理任务 
}catch(Exception ex){ 
}finally{ 
   lock.unlock();   //释放锁 
} 


三、Lock的简单使用:多人卖票案例


       

本例实现3个人卖30张票

import java.util.concurrent.locks.ReentrantLock;
/**
 * Lock练习,3个人卖30张票
 */
//第一步:创建资源类,定义属性和操作方法
class Ticket{
    //票的数量
    private int number = 30;
    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();
    //卖票方法
    public void sale(){
        //上锁
        lock.lock();
        //为了防止在解锁前出现异常而导致不能释放锁的情况,使用finally使解锁一定会执行
        try{
            if (number > 0){
                System.out.println(Thread.currentThread().getName() + "卖出票,还剩" + --number + "张票");
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
}
public class SaleTicket {
    //第二步,创建多个线程,调用资源类中的操作方法
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "售票员A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "售票员B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "售票员C").start();
    }
}


运行结果:

D:\java\jdk\jdk1.8.0_161\bin\java.exe "-javaagent:D:\IntelliJ IDEA 2021.2\lib\idea_rt.jar=49748:D:\IntelliJ IDEA 2021.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Java\jdk\jdk1.8.0_161\jre\lib\charsets.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\deploy.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\access-bridge-32.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\dnsns.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\jaccess.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\localedata.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\nashorn.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\sunec.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\ext\zipfs.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\javaws.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\jce.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\jfr.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\jfxswt.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\jsse.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\management-agent.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\plugin.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\resources.jar;D:\Java\jdk\jdk1.8.0_161\jre\lib\rt.jar;D:\Java\juc\out\production\juc com.shang.lock.SaleTicket
售票员A卖出票,还剩29张票
售票员A卖出票,还剩28张票
售票员A卖出票,还剩27张票
售票员A卖出票,还剩26张票
售票员A卖出票,还剩25张票
售票员A卖出票,还剩24张票
售票员A卖出票,还剩23张票
售票员C卖出票,还剩22张票
售票员C卖出票,还剩21张票
售票员C卖出票,还剩20张票
售票员C卖出票,还剩19张票
售票员C卖出票,还剩18张票
售票员C卖出票,还剩17张票
售票员C卖出票,还剩16张票
售票员C卖出票,还剩15张票
售票员C卖出票,还剩14张票
售票员C卖出票,还剩13张票
售票员C卖出票,还剩12张票
售票员C卖出票,还剩11张票
售票员C卖出票,还剩10张票
售票员C卖出票,还剩9张票
售票员C卖出票,还剩8张票
售票员C卖出票,还剩7张票
售票员C卖出票,还剩6张票
售票员C卖出票,还剩5张票
售票员C卖出票,还剩4张票
售票员C卖出票,还剩3张票
售票员C卖出票,还剩2张票
售票员C卖出票,还剩1张票
售票员C卖出票,还剩0张票
Process finished with exit code 0


相关文章
|
8天前
|
Java 调度 开发者
Java线程池ExecutorService学习和使用
通过学习和使用Java中的 `ExecutorService`,可以显著提升并发编程的效率和代码的可维护性。合理配置线程池参数,结合实际应用场景,可以实现高效、可靠的并发处理。希望本文提供的示例和思路能够帮助开发者深入理解并应用 `ExecutorService`,实现更高效的并发程序。
30 10
|
2月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
57 4
|
3月前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
【10月更文挑战第6天】在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
38 2
|
4月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
|
3月前
|
Java C++
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
48 0
|
4月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
301 6
|
4月前
|
Java
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
53 7
|
5月前
|
Java
在Java多线程领域,精通Lock接口是成为高手的关键。
在Java多线程领域,精通Lock接口是成为高手的关键。相较于传统的`synchronized`,Lock接口自Java 5.0起提供了更灵活的线程同步机制,包括可中断等待、超时等待及公平锁选择等高级功能。本文通过实战演练介绍Lock接口的核心实现——ReentrantLock,并演示如何使用Condition进行精确线程控制,帮助你掌握这一武林秘籍,成为Java多线程领域的盟主。示例代码展示了ReentrantLock的基本用法及Condition在生产者-消费者模式中的应用,助你提升程序效率和稳定性。
62 2
|
5月前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
34 2
|
5月前
|
Java API 调度
JUC线程池: FutureTask详解
总而言之,FutureTask是Java并发编程中一个非常实用的类,它在异步任务执行及结果处理方面提供了优雅的解决方案。在实现细节方面可以搭配线程池的使用,以及与Callable接口的配合使用,来完成高效的并发任务执行和结果处理。
55 0