Java线程安全问题与同步锁

ゝ一纸荒年。 2023-10-17 11:47 83阅读 0赞

Java线程安全问题与同步锁

一篇博文认识Java多线程大致认识了线程也知道了线程的创建方法。今天再来探讨一下线程的安全与同步锁问题。
场景:模拟三个售票员卖30张票
一、测试继承方式创建线程

1)、线程类

  1. package com.hk.java.thread;
  2. /**
  3. * 继承创建法
  4. *
  5. * @author 浪丶荡
  6. *
  7. */
  8. public class OneConductor extends Thread {
  9. static int tickets = 30;// 30张票
  10. @Override
  11. public void run() {
  12. // 重写run方法
  13. while (tickets > 0) {
  14. System.out.println("售票员" + Thread.currentThread().getName()
  15. + "售出了第" + tickets + "张票");
  16. tickets--;
  17. //线程跑的太快了,不好观察结果,让其等待半秒
  18. try {
  19. Thread.sleep(50);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. }

2)、测试类

  1. @Test
  2. public void testThread_Thread(){
  3. OneConductor conductor_1 = new OneConductor();
  4. conductor_1.setName("张三");
  5. OneConductor conductor_2 = new OneConductor();
  6. conductor_2.setName("李四");
  7. OneConductor conductor_3 = new OneConductor();
  8. conductor_3.setName("王五");
  9. conductor_1.start();//张三开始卖票
  10. conductor_2.start();//李四开始卖票
  11. conductor_3.start();//王五开始卖票
  12. }

运行结果:

  1. 售票员 [ 张三 ]售出了第30张票
  2. 售票员 [ 张三 ]售出了第29张票
  3. 售票员 [ 张三 ]售出了第28张票
  4. 售票员 [ 张三 ]售出了第27张票
  5. 售票员 [ 张三 ]售出了第26张票
  6. 售票员 [ 张三 ]售出了第25张票
  7. 售票员 [ 张三 ]售出了第24张票
  8. 售票员 [ 张三 ]售出了第23张票
  9. 售票员 [ 张三 ]售出了第22张票
  10. 售票员 [ 张三 ]售出了第21张票
  11. 售票员 [ 张三 ]售出了第20张票
  12. 售票员 [ 王五 ]售出了第20张票
  13. 售票员 [ 王五 ]售出了第18张票
  14. 售票员 [ 王五 ]售出了第17张票
  15. 售票员 [ 王五 ]售出了第16张票
  16. 售票员 [ 王五 ]售出了第15张票
  17. 售票员 [ 王五 ]售出了第14张票
  18. 售票员 [ 王五 ]售出了第13张票
  19. 售票员 [ 王五 ]售出了第12张票
  20. 售票员 [ 王五 ]售出了第11张票
  21. 售票员 [ 王五 ]售出了第10张票
  22. 售票员 [ 王五 ]售出了第9张票
  23. 售票员 [ 王五 ]售出了第8张票
  24. 售票员 [ 王五 ]售出了第7张票
  25. 售票员 [ 王五 ]售出了第6张票
  26. 售票员 [ 王五 ]售出了第5张票
  27. 售票员 [ 王五 ]售出了第4张票
  28. 售票员 [ 张三 ]售出了第19张票
  29. 售票员 [ 王五 ]售出了第3张票
  30. 售票员 [ 张三 ]售出了第2张票
  31. 售票员 [ 王五 ]售出了第1张票
  32. 售票员 [ 李四 ]售出了第1张票

可以看出第20、第1张票被卖了两次,这显然是不行的,产生的原因就是tickets这个资源时被两个线程使用了

解决办法——加同步锁

  1. package com.hk.java.thread;
  2. /**
  3. * 继承创建法
  4. *
  5. * @author 浪丶荡
  6. *
  7. */
  8. public class OneConductor extends Thread {
  9. static int tickets = 30;// 30张票
  10. @Override
  11. public void run() {
  12. // 重写run方法
  13. synchronized (this) {
  14. //代码块添加同步锁
  15. while (tickets > 0) {
  16. System.out.println("售票员 [ " + Thread.currentThread().getName()
  17. + " ]售出了第" + tickets + "张票");
  18. tickets--;
  19. }
  20. }
  21. }
  22. }

这样做了你会发现——然并卵

为什么呢?synchronized (this)这个this是获取到这个运行权的线程,而我们的线程有三个,也就是说你一扇门有三把锁,三把锁都可以打开,这还是不安全,这就要求我们加一把唯一的锁,可以防火防盗防学长的锁,这个锁就是当前类的字节码文件!

  1. package com.hk.java.thread;
  2. /**
  3. * 继承创建法
  4. *
  5. * @author 浪丶荡
  6. *
  7. */
  8. public class OneConductor extends Thread {
  9. static int tickets = 30;// 30张票
  10. @Override
  11. public void run() {
  12. // 重写run方法
  13. synchronized (OneConductor.class) {
  14. //当前类的字节码作为锁
  15. while (tickets > 0) {
  16. System.out.println("售票员 [ " + Thread.currentThread().getName()
  17. + " ]售出了第" + tickets + "张票");
  18. tickets--;
  19. }
  20. }
  21. }
  22. }

看结果

  1. 售票员 [ 张三 ]售出了第30张票
  2. 售票员 [ 张三 ]售出了第29张票
  3. 售票员 [ 张三 ]售出了第28张票
  4. 售票员 [ 张三 ]售出了第27张票
  5. 售票员 [ 张三 ]售出了第26张票
  6. 售票员 [ 张三 ]售出了第25张票
  7. 售票员 [ 张三 ]售出了第24张票
  8. 售票员 [ 张三 ]售出了第23张票
  9. 售票员 [ 张三 ]售出了第22张票
  10. 售票员 [ 张三 ]售出了第21张票
  11. 售票员 [ 张三 ]售出了第20张票
  12. 售票员 [ 张三 ]售出了第19张票
  13. 售票员 [ 张三 ]售出了第18张票
  14. 售票员 [ 张三 ]售出了第17张票
  15. 售票员 [ 张三 ]售出了第16张票
  16. 售票员 [ 张三 ]售出了第15张票
  17. 售票员 [ 张三 ]售出了第14张票
  18. 售票员 [ 张三 ]售出了第13张票
  19. 售票员 [ 张三 ]售出了第12张票
  20. 售票员 [ 张三 ]售出了第11张票
  21. 售票员 [ 张三 ]售出了第10张票
  22. 售票员 [ 王五 ]售出了第9张票
  23. 售票员 [ 王五 ]售出了第8张票
  24. 售票员 [ 王五 ]售出了第7张票
  25. 售票员 [ 王五 ]售出了第6张票
  26. 售票员 [ 王五 ]售出了第5张票
  27. 售票员 [ 王五 ]售出了第4张票
  28. 售票员 [ 王五 ]售出了第3张票
  29. 售票员 [ 王五 ]售出了第2张票
  30. 售票员 [ 王五 ]售出了第1张票

经过本人多轮测试,通过继承 Thread 类创建的线程必须的使用字节码锁才能保证数据安全,而通过实现Runnable 接口创建的线程加this锁或者字节码锁都可以保证数据安全,其中缘由不是很清楚,所以在使用同步时推荐使用Runnable 接口创建的线程

如果有知道其中缘由的大牛请不吝赐教——qq群:511906138

发表评论

表情:
评论列表 (有 0 条评论,83人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java线同步

    一、多线程同步 当AB两个线程同时竞争资源时,A线程先获取到资源执行,B线程处于阻塞状态,A线程释放资源后,B线程从挂起处继续执行。 测试代码: public c