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源码之事件监听机制(实现EventListener接口方式)
这篇文章主要介绍了Spring源码之事件监听机制(实现EventListener接口方式),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-08-08
最新评论