• java 多线程笔记二 线程安全问题


    高并发下可能回出现的超卖问题

    运行一下代码,模拟超卖问题,运行后会发现会出现重复编号的票

    1. public class Test {
    2. public static void main(String[] args) {
    3. /**
    4. * 模拟出售电影票
    5. */
    6. PiaoThread piaoThread = new PiaoThread();
    7. new Thread(piaoThread,"1号窗口").start();
    8. new Thread(piaoThread,"2号窗口").start();
    9. new Thread(piaoThread,"3号窗口").start();
    10. new Thread(piaoThread,"4号窗口").start();
    11. }
    12. }
    13. class PiaoThread implements Runnable {
    14. //多个窗口使用的是同一个对象,所以成员变量是共享的
    15. private int piao = 100;
    16. @Override
    17. public void run() {
    18. while (true){
    19. if (piao < 1){ break;}
    20. System.out.println(Thread.currentThread().getName()+"卖出了第"+piao+"编号的票");
    21. piao--;
    22. }
    23. }
    24. }

    同步代码块可以解决超卖问题

    1. public class Test {
    2. public static void main(String[] args) {
    3. /**
    4. * 模拟出售电影票
    5. */
    6. PiaoThread piaoThread = new PiaoThread();
    7. new Thread(piaoThread,"1号窗口").start();
    8. new Thread(piaoThread,"2号窗口").start();
    9. new Thread(piaoThread,"3号窗口").start();
    10. new Thread(piaoThread,"4号窗口").start();
    11. }
    12. }
    13. class PiaoThread implements Runnable {
    14. //多个窗口使用的是同一个对象,所以成员变量是共享的
    15. private int piao = 100;
    16. @Override
    17. public void run() {
    18. while (true){
    19. //悲观锁
    20. // synchronized (锁对象),
    21. // 需要是同一个对象,这里都是使用的一个对象,所以可以使用this
    22. synchronized (this){
    23. if (piao < 1){ break;}
    24. System.out.println(Thread.currentThread().getName()+"卖出了第"+piao+"编号的票");
    25. piao--;
    26. }
    27. }
    28. }
    29. }

    同步方法,我们把需要同步的代码块抽取出来,idea抽取方法快捷键ctrl+alt+m

    同步方法的锁对象:

    如果是非静态同步方法,锁对象是this

    如果是静态方法那么锁对象是该方法所在类的字节码对象(类名.class)

    场景:a线程使用了同步代码块,b线程使用的同步方法,这时候ab线程需要同步那么就需要知道同步方法的锁对象是谁。

    1. public class Test {
    2. public static void main(String[] args) {
    3. /**
    4. * 模拟出售电影票
    5. */
    6. PiaoThread piaoThread = new PiaoThread();
    7. new Thread(piaoThread,"1号窗口").start();
    8. new Thread(piaoThread,"2号窗口").start();
    9. new Thread(piaoThread,"3号窗口").start();
    10. new Thread(piaoThread,"4号窗口").start();
    11. }
    12. }
    13. class PiaoThread implements Runnable {
    14. //多个窗口使用的是同一个对象,所以成员变量是共享的
    15. private int piao = 100;
    16. @Override
    17. public void run() {
    18. while (true){
    19. if (extracted()) break;
    20. }
    21. }
    22. private synchronized boolean extracted() {
    23. if (piao < 1){
    24. return true;
    25. }
    26. System.out.println(Thread.currentThread().getName()+"卖出了第"+piao+"编号的票");
    27. piao--;
    28. return false;
    29. }
    30. }

    lock锁解决超卖问题

    1. public class Test {
    2. public static void main(String[] args) {
    3. /**
    4. * 模拟出售电影票
    5. */
    6. PiaoThread piaoThread = new PiaoThread();
    7. new Thread(piaoThread,"1号窗口").start();
    8. new Thread(piaoThread,"2号窗口").start();
    9. new Thread(piaoThread,"3号窗口").start();
    10. new Thread(piaoThread,"4号窗口").start();
    11. }
    12. }
    13. class PiaoThread implements Runnable {
    14. //多个窗口使用的是同一个对象,所以成员变量是共享的
    15. private int piao = 100;
    16. //创建一个lock
    17. private Lock lock = new ReentrantLock();
    18. @Override
    19. public void run() {
    20. while (true){
    21. lock.lock();//加锁
    22. if (piao < 1){
    23. lock.unlock();//释放锁
    24. break;
    25. }
    26. System.out.println(Thread.currentThread().getName()+"卖出了第"+piao+"编号的票");
    27. piao--;
    28. lock.unlock();//释放锁
    29. }
    30. }
    31. }

    多线程下的可见性问题

    出现可见性问题的根本原因:java的内存模型(JMM),java所有共享变量都是存储在主内存中的,在线程执行的时候,线程有单独的工作内存,这个时候会把主内存中的共享变量拷贝一份到工作变量,并且对所有的变量操作都是在工作变量进行的。

    复习可见性问题代码:一下代码我们的理想结果是子线程3秒后修改flag的值,然后主线程结束死循环,运行程序后发现结束不了死循环。下面主线程代码过于简单,运行速度很快,来不及去读主内存中的共享变量,所以一直读的工作内存变量。

    1. public class Test2 {
    2. public static void main(String[] args) {
    3. /**
    4. * 可见性问题
    5. */
    6. MyThread myThread = new MyThread();
    7. new Thread(myThread).start();
    8. //主线程
    9. while (true){
    10. if (MyThread.flag){
    11. System.out.println("flag为true结束主线程死循环");
    12. break;
    13. }
    14. }
    15. }
    16. }
    17. class MyThread implements Runnable{
    18. static boolean flag = false;
    19. @Override
    20. public void run() {
    21. try {
    22. Thread.sleep(3000);
    23. } catch (InterruptedException e) {
    24. e.printStackTrace();
    25. }
    26. flag = true;
    27. System.out.println("修改了flag为true");
    28. }
    29. }

     解决多线程安全问题之可见性问题方法:volatile关键字

    1. public class Test2 {
    2. public static void main(String[] args) {
    3. /**
    4. * 可见性问题
    5. */
    6. MyThread myThread = new MyThread();
    7. new Thread(myThread).start();
    8. //主线程
    9. while (true){
    10. if (MyThread.flag){
    11. System.out.println("flag为true结束主线程死循环");
    12. break;
    13. }
    14. }
    15. }
    16. }
    17. class MyThread implements Runnable{
    18. //这里加上volatile关键字
    19. static volatile boolean flag = false;
    20. @Override
    21. public void run() {
    22. try {
    23. Thread.sleep(3000);
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. flag = true;
    28. System.out.println("修改了flag为true");
    29. }
    30. }

    多线程安全性问题之有序性问题

    多线程情况下编译器对代码重排,会产生不同的结果,引发安全性问题。

    volatile关键字也可以解决有序性问题


    多线程安全性问题之原子性问题

  • 相关阅读:
    想跳槽进大厂?那你得看这份《上千页的Linux源码笔记》
    Linux常用命令
    如何选一款软件助力企业腾飞
    [附源码]计算机毕业设计校园招聘系统Springboot程序
    裸辞后吊打大厂面试官,四面拿到阿里offer后我还是选择了美团
    智慧物联仓储可视化管理系统
    【Linux学习】- POSIX多线程技术
    人际关系和心理活动机制总结 -- 宁向东的清华管理学课总结
    机器学习第10天:集成学习
    【LeetCode周赛】LeetCode第362场周赛
  • 原文地址:https://blog.csdn.net/qq_42572322/article/details/125529601