当前位置:   article > 正文

Java - 多线程+锁机制实现简单模拟抢票_java抢票实现思路

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)不用锁的情况,会出现重卖问题,应避免重卖还有超卖问题

  1. public class Main {
  2. public static void main(String[] args) {
  3. DiyThread diyThread = new DiyThread();
  4. Thread t1 = new Thread(diyThread,"窗口A");
  5. Thread t2 = new Thread(diyThread,"窗口B");
  6. Thread t3 = new Thread(diyThread,"窗口C");
  7. Thread t4 = new Thread(diyThread,"窗口D");
  8. t1.start();
  9. t2.start();
  10. t3.start();
  11. t4.start();
  12. }
  13. }
  14. class DiyThread implements Runnable {
  15. // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
  16. private int count = 100;
  17. // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
  18. // private static int count = 100;
  19. public void run() {
  20. while (true) {
  21. if (count > 0) {
  22. // 不用锁的情况,会出现重卖问题
  23. System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
  24. count--;
  25. try {
  26. // 睡眠50毫秒
  27. Thread.sleep(50);
  28. } catch (Exception e) {
  29. System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
  30. }
  31. } else {
  32. System.out.println(Thread.currentThread().getName() + " : 票已售完");
  33. break;
  34. }
  35. }
  36. }
  37. }

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

  1. public class Main {
  2. public static void main(String[] args) {
  3. DiyThread diyThread = new DiyThread();
  4. Thread t1 = new Thread(diyThread,"窗口A");
  5. Thread t2 = new Thread(diyThread,"窗口B");
  6. Thread t3 = new Thread(diyThread,"窗口C");
  7. Thread t4 = new Thread(diyThread,"窗口D");
  8. t1.start();
  9. t2.start();
  10. t3.start();
  11. t4.start();
  12. }
  13. }
  14. class DiyThread implements Runnable {
  15. // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
  16. private int count = 100;
  17. // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
  18. // private static int count = 100;
  19. public void run() {
  20. while (true) {
  21. synchronized(this) {
  22. if (count > 0) {
  23. System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
  24. count--;
  25. try {
  26. // 睡眠50毫秒
  27. Thread.sleep(50);
  28. } catch (Exception e) {
  29. System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
  30. }
  31. } else {
  32. System.out.println(Thread.currentThread().getName() + " : 票已售完");
  33. break;
  34. }
  35. }
  36. }
  37. }
  38. }

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

  1. public class Main {
  2. public static void main(String[] args) {
  3. DiyThread diyThread = new DiyThread();
  4. Thread t1 = new Thread(diyThread,"窗口A");
  5. Thread t2 = new Thread(diyThread,"窗口B");
  6. Thread t3 = new Thread(diyThread,"窗口C");
  7. Thread t4 = new Thread(diyThread,"窗口D");
  8. t1.start();
  9. t2.start();
  10. t3.start();
  11. t4.start();
  12. }
  13. }
  14. class DiyThread implements Runnable {
  15. // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
  16. private int count = 100;
  17. // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
  18. // private static int count = 100;
  19. public void run() {
  20. sale();
  21. }
  22. private synchronized void sale() {
  23. while (true) {
  24. if (count > 0) {
  25. System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
  26. count--;
  27. try {
  28. // 睡眠50毫秒
  29. Thread.sleep(50);
  30. } catch (Exception e) {
  31. System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
  32. }
  33. } else {
  34. System.out.println(Thread.currentThread().getName() + " : 票已售完");
  35. break;
  36. }
  37. }
  38. }
  39. }

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

  1. import java.util.concurrent.locks.Lock;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class Main {
  4. public static void main(String[] args) {
  5. DiyThread diyThread = new DiyThread();
  6. Thread t1 = new Thread(diyThread,"窗口A");
  7. Thread t2 = new Thread(diyThread,"窗口B");
  8. Thread t3 = new Thread(diyThread,"窗口C");
  9. Thread t4 = new Thread(diyThread,"窗口D");
  10. t1.start();
  11. t2.start();
  12. t3.start();
  13. t4.start();
  14. }
  15. }
  16. class DiyThread implements Runnable {
  17. // 成员变量(实例变量):随着对象的创建而存在,随着对象的回收而释放,存储在堆内存的对象中
  18. private int count = 100;
  19. // 静态变量(类变量):随着类的加载而存在,随着类的消失而消失,存储在方法区(共享数据区)的静态区
  20. // private static int count = 100;
  21. private Lock lock = new ReentrantLock();
  22. public void run() {
  23. while (true) {
  24. lock.lock(); // 加锁之后,以下的业务代码就是单线程环境运行,如4个线程竞争这把锁
  25. try {
  26. if (count > 0) {
  27. System.out.println(Thread.currentThread().getName() + " -> 售出第" + count + "张火车票");
  28. count--;
  29. Thread.sleep(50);
  30. } else {
  31. System.out.println(Thread.currentThread().getName() + " : 票已售完");
  32. break;
  33. }
  34. } catch (Exception e) {
  35. System.out.println(Thread.currentThread().getName() + " : 出现异常 -> " + e.getMessage());
  36. } finally {
  37. lock.unlock(); // 解锁,最重要原因为避免死锁,无论正确、异常执行,都执行解锁
  38. }
  39. }
  40. }
  41. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/632196
推荐阅读
相关标签
  

闽ICP备14008679号