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

相关文章

  • 解决idea不显示Services工具栏的问题

    解决idea不显示Services工具栏的问题

    这篇文章主要介绍了解决idea不显示Services工具栏的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Springboot+Netty+Websocket实现消息推送实例

    Springboot+Netty+Websocket实现消息推送实例

    这篇文章主要介绍了Springboot+Netty+Websocket实现消息推送实例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • mybatisplus的坑 insert标签insert into select无参数问题的解决

    mybatisplus的坑 insert标签insert into select无参数问题的解决

    这篇文章主要介绍了mybatisplus的坑 insert标签insert into select无参数问题的解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • springboot自动重启的简单方法

    springboot自动重启的简单方法

    Springboot提供了热部署的方式,当发现任何类发生了改变,马上通过JVM类加载的方式,加载最新的类到虚拟机中。这篇文章主要介绍了springboot自动重启的实现方法,需要的朋友可以参考下
    2018-04-04
  • 关于Java错误提示之找不到或无法加载主类的问题及正确处理方法

    关于Java错误提示之找不到或无法加载主类的问题及正确处理方法

    当我们在初学Java的是时候,类文件中是不设定包名(package)的,这种情况下注意classpath,基本上没有问题, 本文主要说明classpath和系统环境变量PATH都没问题的情况下出错原因和正确处理方法,感兴趣的朋友一起看看吧
    2022-01-01
  • Java中接口Set的特点及方法说明

    Java中接口Set的特点及方法说明

    这篇文章主要介绍了Java中接口Set的特点及方法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • IDEA MyBatis Plugins自动生成实体类和mapper.xml

    IDEA MyBatis Plugins自动生成实体类和mapper.xml

    这篇文章主要介绍了IDEA MyBatis Plugins自动生成实体类和mapper.xml,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 通过Spring自定义NamespaceHandler实现命名空间解析(推荐)

    通过Spring自定义NamespaceHandler实现命名空间解析(推荐)

    这篇文章主要介绍了通过Spring自定义NamespaceHandler实现命名空间解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Java实现矩阵顺时针旋转90度的示例

    Java实现矩阵顺时针旋转90度的示例

    今天小编就为大家分享一篇Java实现矩阵顺时针旋转90度的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • Maven本地jar引用的实现方法

    Maven本地jar引用的实现方法

    这篇文章主要介绍了Maven本地jar引用的实现方法的相关资料,希望通过本文能帮助到大家,实现这样的功能,需要的朋友可以参考下
    2017-10-10

最新评论