Java多线程+锁机制实现简单模拟抢票的项目实践

 更新时间:2024年02月03日 09:42:18   作者:帅龍之龍  
锁是一种同步机制,用于控制对共享资源的访问,在线程获取到锁对象后,可以执行抢票操作,本文主要介绍了Java多线程+锁机制实现简单模拟抢票的项目实践,具有一定的参考价值,感兴趣的可以了解一下

前言

锁是一种同步机制,用于控制对共享资源的访问。锁的作用是确保同一时间只有一个线程可以访问共享资源,也就是说保证了线程安全。因此在并发编程中,锁是相当重要的。

一、基本概念

1.为什么需要锁?

(1)多任务环境中才需要;
(2)任务都需要对同一共享资源进行写操作;
(3)对资源的访问是互斥的;

2.Syncronized与Lock的区别

(1)syncronized是jvm层面的内置关键字,lock是java的一个接口;
(2)syncronized实现线程同步时,若线程一阻塞,线程二则一直等待,lock则不会,会自动结束线程;
(3)syncronized会自动释放锁,lock需要手动在finally里释放(unlock),syncronized无法判断是否获得锁的状态,lock可以;
(4)syncronized的锁可重入、不可中断、非公平,lock的锁可重入、可中断、公平;
(5)lock适合大量同步代码的同步问题,syncronized适合少量;

3.常见的锁

(1)sychronized:非公平、悲观、独享、互斥、可重入的重量级锁
(2)ReentrantLock:默认非公平但可实现公平的、悲观、独享、互斥、可重入、重量级锁
(3)ReentrantReadWriteLock:默认非公平但是可实现公平的、悲观、写独享、读共享、读写、可重入、重量级锁

4.缺点

jvm锁解决不了分布式环境多任务对共享资源竞争的协同操作问题。

二、模拟抢票

(1)不用锁的情况,会出现重卖问题,应避免重卖还有超卖问题

public class Main {
    public static void main(String[] args) {
        DiyThread diyThread = new DiyThread();
        Thread t1 = new Thread(diyThread,"窗口A");
        Thread t2 = new Thread(diyThread,"窗口B");
        Thread t3 = new Thread(diyThread,"窗口C");
        Thread t4 = new Thread(diyThread,"窗口D");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class DiyThread implements Runnable {
    // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
    private int count = 100;

    // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
    // private static int count = 100;

    public void run() {
        while (true) {
            if (count > 0) {
                // 不用锁的情况,会出现重卖问题
                System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
                count--;
                try {
                    // 睡眠50毫秒
                    Thread.sleep(50);
                } catch (Exception e) {
                    System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " : 票已售完");
                break;
            }
        }
    }
}

(2)使用【同步代码块】使线程串行同步,不然会出现线程不安全的问题

public class Main {
    public static void main(String[] args) {
        DiyThread diyThread = new DiyThread();
        Thread t1 = new Thread(diyThread,"窗口A");
        Thread t2 = new Thread(diyThread,"窗口B");
        Thread t3 = new Thread(diyThread,"窗口C");
        Thread t4 = new Thread(diyThread,"窗口D");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class DiyThread implements Runnable {
    // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
    private int count = 100;

    // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
    // private static int count = 100;

    public void run() {
        while (true) {
            synchronized(this) {
                if (count > 0) {
                    System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
                    count--;
                    try {
                        // 睡眠50毫秒
                        Thread.sleep(50);
                    } catch (Exception e) {
                        System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + " : 票已售完");
                    break;
                }
            }
        }
    }
}

(3)使用【synchronized】同步函数使线程串行同步,不然会出现线程不安全的问题

public class Main {
    public static void main(String[] args) {
        DiyThread diyThread = new DiyThread();
        Thread t1 = new Thread(diyThread,"窗口A");
        Thread t2 = new Thread(diyThread,"窗口B");
        Thread t3 = new Thread(diyThread,"窗口C");
        Thread t4 = new Thread(diyThread,"窗口D");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class DiyThread implements Runnable {
    // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
    private int count = 100;

    // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
    // private static int count = 100;

    public void run() {
        sale();
    }

    private synchronized void sale() {
        while (true) {
            if (count > 0) {
                System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
                count--;
                try {
                    // 睡眠50毫秒
                    Thread.sleep(50);
                } catch (Exception e) {
                    System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " : 票已售完");
                break;
            }
        }
    }
}

(4)使用【java.util.concurrent】简称juc包的lock锁

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

public class Main {
    public static void main(String[] args) {
        DiyThread diyThread = new DiyThread();
        Thread t1 = new Thread(diyThread,"窗口A");
        Thread t2 = new Thread(diyThread,"窗口B");
        Thread t3 = new Thread(diyThread,"窗口C");
        Thread t4 = new Thread(diyThread,"窗口D");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class DiyThread implements Runnable {
    // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
    private int count = 100;

    // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
    // private static int count = 100;

    private Lock lock = new ReentrantLock();

    public void run() {
        while (true) {
            lock.lock(); // 加锁之后,以下的业务代码就是单线程环境运行,如4个线程竞争这把锁
            try {
                if (count > 0) {
                    System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
                    count--;
                    Thread.sleep(50);
                } else {
                    System.out.println(Thread.currentThread().getName() + " : 票已售完");
                    break;
                }
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
            } finally {
                lock.unlock(); // 解锁,最重要原因为避免死锁,无论正确、异常执行,都执行解锁
            }
        }
    }
}

到此这篇关于Java多线程+锁机制实现简单模拟抢票的项目实践的文章就介绍到这了,更多相关Java 模拟抢票内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:

相关文章

  • Spring Cloud分布式定时器之ShedLock的实现

    Spring Cloud分布式定时器之ShedLock的实现

    这篇文章主要介绍了Spring Cloud分布式定时器之ShedLock的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • java LeetCode刷题稍有难度的贪心构造算法

    java LeetCode刷题稍有难度的贪心构造算法

    这篇文章主要为大家介绍了java LeetCode刷题稍有难度的贪心构造题解示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Java interrupt()方法使用实例介绍

    Java interrupt()方法使用实例介绍

    一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果比如会带着自己所持有的锁而永远的休眠,迟迟不归还锁等。 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了
    2023-02-02
  • Java ArrayList与LinkedList及HashMap容器的用法区别

    Java ArrayList与LinkedList及HashMap容器的用法区别

    这篇文章主要介绍了Java ArrayList与LinkedList及HashMap容器的用法区别,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-07-07
  • Java垃圾回收之标记压缩算法详解

    Java垃圾回收之标记压缩算法详解

    今天小编就为大家分享一篇关于Java垃圾回收之标记压缩算法详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • SSO单点登录系统实现原理及流程图解

    SSO单点登录系统实现原理及流程图解

    这篇文章主要介绍了SSO单点登录系统实现原理及流程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Java中的Sentinel规则持久化详解

    Java中的Sentinel规则持久化详解

    这篇文章主要介绍了Java中的Sentinel规则持久化详解,将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效,需要的朋友可以参考下
    2023-09-09
  • Mybatis 条件查询 批量增删改查功能

    Mybatis 条件查询 批量增删改查功能

    这篇文章主要介绍了mybatis 脚本处理语句之条件查询 批量增删改查功能,需要的的朋友参考下吧
    2017-06-06
  • Spring Boot 通过注解实现数据校验的方法

    Spring Boot 通过注解实现数据校验的方法

    这篇文章主要介绍了Spring Boot 通过注解实现数据校验的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • SpringBoot超详细讲解yaml配置文件

    SpringBoot超详细讲解yaml配置文件

    这篇文章主要介绍了SpringBoot中的yaml配置文件问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06

最新评论