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 模拟抢票内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:

相关文章

  • SpringCloud之动态刷新、重试、服务化的实现

    SpringCloud之动态刷新、重试、服务化的实现

    这篇文章主要介绍了SpringCloud 之动态刷新、重试、服务化的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Spring源码之事件监听机制(实现EventListener接口方式)

    Spring源码之事件监听机制(实现EventListener接口方式)

    这篇文章主要介绍了Spring源码之事件监听机制(实现EventListener接口方式),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 详解SpringMVC中的异常处理

    详解SpringMVC中的异常处理

    这篇文章主要介绍了SpringMVC中的异常处理的相关资料,帮助大家更好的理解和学习使用SpringMVC,感兴趣的朋友可以了解下
    2021-03-03
  • Java Socket实现猜数字小游戏

    Java Socket实现猜数字小游戏

    这篇文章主要为大家详细介绍了Java Socket实现猜数字小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-09-09
  • Java并发的CAS原理与ABA问题的讲解

    Java并发的CAS原理与ABA问题的讲解

    今天小编就为大家分享一篇关于Java并发的CAS原理与ABA问题的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • java中DES加密解密

    java中DES加密解密

    本文给大家分享的是一段java中实现des加密解密的代码,非常的实用,基本每个项目都可以用到,推荐给大家。
    2015-03-03
  • 详解Java中StringBuffer类常用方法

    详解Java中StringBuffer类常用方法

    这篇文章主要为大家介绍了java中StringBuffer类常用方法
    2016-01-01
  • 基于Java swing组件实现简易计算器

    基于Java swing组件实现简易计算器

    这篇文章主要介绍了基于Java swing组件实现简易计算器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Java线程通信中关于生产者与消费者案例分析

    Java线程通信中关于生产者与消费者案例分析

    这篇文章主要介绍了Java线程通信中关于生产者与消费者案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • logback自定义日志与Nacos日志冲突问题

    logback自定义日志与Nacos日志冲突问题

    这篇文章主要介绍了logback自定义日志与Nacos日志冲突问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04

最新评论