• 多线程编程:一个指令重排序引发的chaos


    先贴出正确的代码:

     1 package com.xiaobai.thread.main;
     2 
     3 import lombok.extern.slf4j.Slf4j;
     4 
     5 @Slf4j
     6 public class ThreadMain {
     7 
     8     private volatile static int ticket;
     9 
    10     private static class RunningTask implements Runnable{
    11 
    12         public RunningTask(boolean proOrSell) {
    13             this.proOrSell = proOrSell;
    14         }
    15 
    16         private boolean proOrSell = true;//如果为静态,会被实例共享
    17 
    18         public void run() {
    19             while (true) {//只同步需要同步的代码,while(true)不能放在同步里面!!否则除非线程自己wait(),永远不会轮到其他线程!!
    20                 synchronized (RunningTask.class) {
    21                     proOrSell();
    22                 }
    23             }
    24         }
    25 
    26         private void proOrSell() {
    27             if(proOrSell) {
    28                 ticket++;
    29                 log.info("Tickets/Pro:" + ticket);
    30                 RunningTask.class.notifyAll();
    31                 if(ticket >= 1000) {
    32                     try {
    33                         System.out.println("--------------------------------------------------------------------------------------------------------------------------------------->Pro Wait!!!!!");
    34                         RunningTask.class.wait();//持有锁的本线程等待
    35                     } catch (InterruptedException e) {
    36 
    37                     }
    38                 }
    39             }else {
    40                 System.out.println(Thread.currentThread().getName() + " take control!");
    41                 if(ticket <= 100) {
    42                     try {
    43                         System.out.println(Thread.currentThread().getName() + " <=100!");
    44                         System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!");
    45                         RunningTask.class.wait();
    46                     } catch (InterruptedException e) {
    47 
    48                     }
    49                 }else {
    50                     ticket--;
    51                     log.info("Tickets------->Sell:" + ticket);
    52                     System.out.println(Thread.currentThread().getName() + " decrease tickets!");
    53                     if(ticket <= 500) {
    54                         RunningTask.class.notifyAll();
    55                     }
    56                 }
    57             }
    58         }
    59     }
    60 
    61     private static void proOrSell() {
    62         Runnable task1 = new RunningTask(true);
    63         Runnable task2 = new RunningTask(false);
    64         Thread thread1 = new Thread(task1);
    65         Thread thread2 = new Thread(task2);
    66         Thread thread3 = new Thread(task2);
    67         Thread thread4 = new Thread(task2);
    68         thread1.start();
    69         try {
    70             Thread.sleep(3000);
    71         } catch (InterruptedException e) {
    72 
    73         }
    74         thread2.start();
    75         thread3.start();
    76         thread4.start();
    77     }
    78 
    79     public static void main(String[] args) {
    80         proOrSell();
    81         while (ticket <= 0) {
    82             System.out.println("------------------------------------------------------->Ticket System Broken!!!!!!");
    83             System.exit(0);
    84         }
    85     }
    86 
    87 }
    View Code

    代码简单说明:

    1.多线程生产者-消费者程序,一个生产者,3个消费者

    2.同步共享变量操作代码,同一把锁(内部嵌套类的字节码对象)

    3.注意while(true)要在同步代码外面,否则没有意义!

    4.改造的等待-唤醒机制,控制票数在一个下限(100)和一个上限(1000)

    正确代码运行结果摘录:

    1)

    Thread-1 take control!
    2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:105
    Thread-1 decrease tickets!
    Thread-1 take control!
    2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:104
    Thread-1 decrease tickets!
    Thread-1 take control!
    2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:103
    Thread-1 decrease tickets!
    Thread-1 take control!
    2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:102
    Thread-1 decrease tickets!
    Thread-1 take control!
    2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:101
    Thread-1 decrease tickets!
    Thread-1 take control!
    2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:100
    Thread-1 decrease tickets!
    Thread-1 take control!
    Thread-1 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    Thread-3 take control!
    Thread-3 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:101
    2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:102
    2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:103
    2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:104

    2)
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:107
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:106
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:105
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:104
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:103
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:102
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:101
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:100
    Thread-2 decrease tickets!
    Thread-2 take control!
    Thread-2 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    Thread-3 take control!
    Thread-3 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    Thread-1 take control!
    Thread-1 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:101
    2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:102
    2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:103
    2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:104
    2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:105

    3)
    Thread-2 take control!
    2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:104
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:103
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:102
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:101
    Thread-2 decrease tickets!
    Thread-2 take control!
    2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:100
    Thread-2 decrease tickets!
    Thread-2 take control!
    Thread-2 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    Thread-3 take control!
    Thread-3 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    Thread-1 take control!
    Thread-1 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:101
    2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:102
    2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:103
    2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:104

    说明:当票数达到下限100时,三个消费者先后侦测到并在同一个监视器上阻塞等待,由生产者继续屯票,并唤醒消费者继续消费。生产者侦测票数达到1000时暂停生产,阻塞等待。

    错误的程序:将else中嵌套的else中的内容拿到外面,去掉嵌套else.也就是说,无论上面的if条件是否满足,都执行现在在嵌套else中的全部内容。

    错误程序出现的运行现象:

    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:113
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:112
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:111
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:110
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:109
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:108
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:107
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:106
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:105
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:104
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:103
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:102
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:101
    Thread-3 decrease tickets!
    Thread-3 take control!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:100
    Thread-3 decrease tickets!
    Thread-3 take control!
    Thread-3 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 20:53:18.891 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:99
    Thread-2 decrease tickets!
    Thread-2 take control!
    Thread-2 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 20:53:18.891 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:98
    Thread-1 decrease tickets!
    Thread-1 take control!
    Thread-1 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 20:53:18.891 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:97
    Thread-2 decrease tickets!
    Thread-2 take control!
    Thread-2 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets------->Sell:96
    Thread-3 decrease tickets!
    Thread-3 take control!
    Thread-3 <=100!
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:97
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:98
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:99
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:100
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:101
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:102
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:103
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:104
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:105
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:106
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:107
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:108
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:109
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:110
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:111
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:112
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:113
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:114
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:115
    2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
    Tickets/Pro:116

    分析:通过不同程序段打印和线程名打印可以清晰看到,消费者程序段中发生了指令重排序。if段后面的程序被挪到最前面执行,造成<=100也消费的情况。程序段中一致的线程名和上下文没有不同线程名交叉出现的情况说明同步锁正确且生效,是线程内顺序程序段的指令重排序造成了混乱。将if下面的程序段放在嵌套的else中后,程序执行正常,不再出现<=100消费的情况。生产者这边也没有溢出1000的情况发生。

    总结:多线程程序,在注意共享变量操作需要同步需要同步的代码的同时,还需要注意线程内指令重排序造成多线程环境出现混乱的情况,这种情况同样会造成类似非线程安全的情况,但并非线程安全问题,而是同一个线程中的顺序混乱被带到多线程操作中,造成了混乱和不安全。

  • 相关阅读:
    Mina、Netty、Twisted一起学习(三):TCP前缀固定大小的消息(Header)
    集装箱set相关算法
    企业视觉-大型电商(制)-高性能的用户视觉性能(1)
    周期节
    在近排博客活动已被删除几篇文章
    [Python] Different ways to test multiple flags at once in Python
    [Angular] Use :host-context and the ::ng-deep selector to apply context-based styling
    [Javascirpt AST] Babel Plugin -- create new CallExpression
    [Python] Object spread operator in Python
    [Javascript AST] 3. Continue: Write ESLint rule
  • 原文地址:https://www.cnblogs.com/free-wings/p/10093572.html
Copyright © 2020-2023  润新知